From 09d138e229846b7056331151135b7c8a52dc476f Mon Sep 17 00:00:00 2001
From: xsipewe
- A behaviour module for implementing a state machine. Two
+ This behavior module provides a state machine. Two
- This is a new behaviour in OTP-19.0.
- It has been thoroughly reviewed, is stable enough
- to be used by at least two heavy OTP applications, and is here to stay.
- But depending on user feedback, we do not expect
- but might find it necessary to make minor
- not backwards compatible changes into OTP-20.0,
- so its state can be designated as "not quite experimental"...
+ are supported:
One for finite-state machines
+ ( One without restriction on the state data type
+ that uses one callback function for all states
+
+ This is a new behavior in Erlang/OTP 19.0.
+ It has been thoroughly reviewed, is stable enough
+ to be used by at least two heavy OTP applications, and is here to stay.
+ Depending on user feedback, we do not expect
+ but can find it necessary to make minor
+ not backward compatible changes into Erlang/OTP 20.0.
+
- The
The callback model(s) for
A generic state machine process (
A
+
+
-
gen_statem module Callback module ----------------- --------------- @@ -103,37 +112,37 @@ erlang:'!' -----> Module:StateName/3 - -----> Module:code_change/4
Events are of different
-
If a callback function fails or returns a bad value,
- the
The "state function" for a specific
When the
When the
The
@@ -173,73 +182,76 @@ erlang:'!' -----> Module:StateName/3
Inserting an event replaces the trick of calling your own
state handling functions that you often would have to
- resort to in for example
If you in
- See the type
+ For the details of a state transition, see type
- A
- Note that a
Unless otherwise stated, all functions in this module fail if
the specified
The
- This example shows a simple pushbutton model
+ The following example shows a simple pushbutton model
for a toggling pushbutton implemented with
This is the complete callback module file
The following is the complete callback module file
+
-module(pushbutton).
-behaviour(gen_statem).
@@ -305,7 +318,7 @@ handle_event(_, _, Data) ->
%% Ignore all other events
{keep_state,Data}.
- And this is a shell session when running it:
+The following is a shell session when running it:
1> pushbutton:start(). {ok,<0.36.0>} @@ -326,12 +339,11 @@ ok in function gen:do_for_proc/2 (gen.erl, line 261) in call from gen_statem:call/3 (gen_statem.erl, line 386)-
- And just to compare styles here is the same example using
+ To compare styles, here follows the same example using
@@ -364,11 +376,11 @@ handle_event(_, _, State, Data) ->
-
+
Name specification to use when starting
- a gen_statem server. See
+ a gen_statem server. See
start_link/3
@@ -380,7 +392,7 @@ handle_event(_, _, State, Data) ->
-
+
Server specification to use when addressing
@@ -393,75 +405,83 @@ handle_event(_, _, State, Data) ->
It can be:
- pid()
- LocalName
- - The
gen_statem is locally registered.
+ pid() | LocalName
+ -
+
+ The gen_statem is locally registered.
+
+
Name, Node
-
- The
gen_statem is locally registered
- on another node.
+
+ The gen_statem is locally registered
+ on another node.
+
GlobalName
-
- The
gen_statem is globally registered
- in global .
+
+ The gen_statem is globally registered
+ in kernel:global .
+
RegMod, ViaName
-
- The
gen_statem is registered through
- an alternative process registry.
- The registry callback module RegMod
- should export the functions
- register_name/2 , unregister_name/1 ,
- whereis_name/1 and send/2 ,
- which should behave like the corresponding functions
- in global .
- Thus, {via,global,GlobalName} is the same as
- {global,GlobalName} .
+
+ The gen_statem is registered through
+ an alternative process registry.
+ The registry callback module RegMod
+ is to export functions
+ register_name/2 , unregister_name/1 ,
+ whereis_name/1 , and send/2 ,
+ which are to behave like the corresponding functions
+ in kernel:global .
+ Thus, {via,global,GlobalName} is the same as
+ {global,GlobalName} .
+
-
+
Debug option that can be used when starting
- a gen_statem server through for example
+ a gen_statem server through, for example,
enter_loop/5 .
- For every entry in Dbgs
+ For every entry in Dbgs ,
the corresponding function in
- sys will be called.
+ sys is called.
-
+
Options that can be used when starting
- a gen_statem server through for example
+ a gen_statem server through, for example,
start_link/3 .
-
+
- Return value from the start functions for_example
+ Return value from the start functions, for example,
start_link/3 .
-
-
+
- Destination to use when replying through for example the
+ Destination to use when replying through, for example, the
action()
@@ -472,178 +492,194 @@ handle_event(_, _, State, Data) ->
-
+
- After a state change (NextState =/= State )
+ After a state change (NextState =/= State ),
all postponed events are retried.
-
+
If the
callback mode
is state_functions ,
- the state has to be of this type.
+ the state must be of this type.
-
+
A term in which the state machine implementation
- should store any server data it needs. The difference between
+ is to store any server data it needs. The difference between
this and the state()
itself is that a change in this data does not cause
- postponed events to be retried. Hence if a change
+ postponed events to be retried. Hence, if a change
in this data would change the set of events that
- are handled than that data item should be made
+ are handled, then that data item is to be made
a part of the state.
-
+
- External events are of 3 different type:
- {call,From } , cast or info .
+ External events are of three types:
+ {call,From } , cast , or info .
Calls
(synchronous) and
casts
originate from the corresponding API functions.
- For calls the event contain whom to reply to.
+ For calls, the event contains whom to reply to.
Type info originates from regular process messages sent
- to the gen_statem . It is also possible for the state machine
- implementation to generate events of types
+ to the gen_statem . Also, the state machine
+ implementation can generate events of types
timeout and internal to itself.
-
+
The callback mode is selected when starting the
gen_statem using the return value from
Module:init/1
or when calling
- enter_loop/5-7 ,
+ enter_loop/5,6,7 ,
and with the return value from
- Module:code_change/4 .
-
+ Module:code_change/4
+ .
state_functions
-
- The state has to be of type
-
state_name()
- and one callback function per state that is
-
- Module:StateName/3
-
- is used.
+
+ The state must be of type
+ state_name()
+ and one callback function per state, that is,
+
+ Module:StateName/3
+ ,
+ is used.
+
handle_event_function
-
- The state can be any term and the callback function
-
- Module:handle_event/4
-
- is used for all states.
+
+ The state can be any term and the callback function
+
+ Module:handle_event/4
+
+ is used for all states.
+
-
+
- Transition options may be set by
+ Transition options can be set by
actions
- and they modify some details below in how
+ and they modify the following in how
the state transition is done:
-
- All
-
actions
- are processed in order of appearance.
+
+ All
+ actions
+ are processed in order of appearance.
+
-
- If
-
- postpone()
-
- is true
- the current event is postponed.
+
+ If
+
+ postpone()
+
+ is true ,
+ the current event is postponed.
+
-
- If the state changes the queue of incoming events
- is reset to start with the oldest postponed.
+
+ If the state changes, the queue of incoming events
+ is reset to start with the oldest postponed.
+
-
- All events stored with
-
- action()
-
- next_event
- are inserted in the queue to be processed before
- all other events.
+
+ All events stored with
+
+ action()
+
+ next_event
+ are inserted in the queue to be processed before
+ all other events.
+
-
- If an
-
- event_timeout()
-
- is set through
-
- action()
-
- timeout
- an event timer may be started or a timeout zero event
- may be enqueued.
+
+ If an
+
+ event_timeout()
+
+ is set through
+
+ action()
+
+ timeout ,
+ an event timer can be started or a time-out zero event
+ can be enqueued.
+
-
- The (possibly new)
-
state function
- is called with the oldest enqueued event if there is any,
- otherwise the gen_statem goes into receive
- or hibernation
- (if
-
- hibernate()
-
- is true )
- to wait for the next message. In hibernation the next
- non-system event awakens the gen_statem , or rather
- the next incoming message awakens the gen_statem
- but if it is a system event
- it goes right back into hibernation.
+
+ The (possibly new)
+ state function
+ is called with the oldest enqueued event if there is any,
+ otherwise the gen_statem goes into receive
+ or hibernation
+ (if
+
+ hibernate()
+
+ is true )
+ to wait for the next message. In hibernation the next
+ non-system event awakens the gen_statem , or rather
+ the next incoming message awakens the gen_statem ,
+ but if it is a system event
+ it goes right back into hibernation.
+
-
+
- If true postpone the current event and retry
+ If true , postpones the current event and retries
it when the state changes
(NextState =/= State ).
-
+
- If true hibernate the gen_statem
+ If true , hibernates the gen_statem
by calling
proc_lib:hibernate/3
@@ -651,9 +687,9 @@ handle_event(_, _, State, Data) ->
before going into receive
to wait for a new external event.
If there are enqueued events,
- to prevent receiving any new event; a
+ to prevent receiving any new event, an
- garbage_collect/0
+ erlang:garbage_collect/0
is done instead to simulate
that the gen_statem entered hibernation
and immediately got awakened by the oldest enqueued event.
@@ -661,39 +697,39 @@ handle_event(_, _, State, Data) ->
-
+
- Generate an event of
+ Generates an event of
event_type()
timeout
- after this time (in milliseconds) unless some other
- event arrives in which case this timeout is cancelled.
- Note that a retried or inserted event
- counts just like a new in this respect.
+ after this time (in milliseconds) unless another
+ event arrives in which case this time-out is cancelled.
+ Notice that a retried or inserted event
+ counts like a new in this respect.
- If the value is infinity no timer is started since
- it will never trigger anyway.
+ If the value is infinity , no timer is started, as
+ it never triggers anyway.
- If the value is 0 the timeout event is immediately enqueued
- unless there already are enqueued events since then the
- timeout is immediately cancelled.
- This is a feature ensuring that a timeout 0 event
- will be processed before any not yet received external event.
+ If the value is 0 , the time-out event is immediately enqueued
+ unless there already are enqueued events, as the
+ time-out is then immediately cancelled.
+ This is a feature ensuring that a time-out 0 event
+ is processed before any not yet received external event.
- Note that it is not possible nor needed to cancel this timeout
- since it is cancelled automatically by any other event.
+ Notice that it is not possible or needed to cancel this time-out,
+ as it is cancelled automatically by any other event.
-
+
- These state transition actions may be invoked by
+ These state transition actions can be invoked by
returning them from the
state function ,
from Module:init/1
@@ -708,9 +744,9 @@ handle_event(_, _, State, Data) ->
transition options
- overrides any previous of the same type,
+ override any previous of the same type,
so the last in the containing list wins.
- For example the last
+ For example, the last
event_timeout()
@@ -719,77 +755,87 @@ handle_event(_, _, State, Data) ->
postpone
-
- Set the
-
- transition_option()
-
-
- postpone()
-
- for this state transition.
- This action is ignored when returned from
- Module:init/1
- or given to
- enter_loop/5,6
- since there is no event to postpone in those cases.
+
+ Sets the
+
+ transition_option()
+
+
+ postpone()
+
+ for this state transition.
+ This action is ignored when returned from
+ Module:init/1
+ or given to
+ enter_loop/5,6 ,
+ as there is no event to postpone in those cases.
+
hibernate
-
- Set the
-
- transition_option()
-
-
- hibernate()
-
- for this state transition.
+
+ Sets the
+
+ transition_option()
+
+
+ hibernate()
+
+ for this state transition.
+
Timeout
-
- Short for
{timeout,Timeout,Timeout} that is
- the timeout message is the timeout time.
- This form exists to make the
- state function
- return value {next_state,NextState,NewData,Timeout}
- allowed like for
-
- gen_fsm Module:StateName/2 .
-
+
+ Short for {timeout,Timeout,Timeout} , that is,
+ the time-out message is the time-out time.
+ This form exists to make the
+ state function
+ return value {next_state,NextState,NewData,Timeout}
+ allowed like for
+
+ gen_fsm:Module:StateName/2
+ .
+
timeout
-
- Set the
-
- transition_option()
-
-
- event_timeout()
-
- to Time with EventContent .
+
+ Sets the
+
+ transition_option()
+
+
+ event_timeout()
+
+ to Time with EventContent .
+
reply_action()
- - Reply to a caller.
+ -
+
+ Replies to a caller.
+
+
next_event
-
- Store the given
EventType
+
+ Stores the specified EventType
and EventContent for insertion after all
actions have been executed.
-
- -
+
The stored events are inserted in the queue as the next to process
- before any already queued events. The order of these stored events
- is preserved so the first next_event in the containing list
- will become the first to process.
+ before any already queued events. The order of these stored events
+ is preserved, so the first next_event in the containing
+ list becomes the first to process.
-
- -
An event of type
internal
- should be used when you want to reliably distinguish
+ is to be used when you want to reliably distinguish
an event inserted this way from any external event.
@@ -797,294 +843,125 @@ handle_event(_, _, State, Data) ->
-
+
- Reply to a caller waiting for a reply in
+ Replies to a caller waiting for a reply in
call/2 .
- From must be the term from the
+ From must be the term from argument
{call,From }
- argument to the
+ to the
state function .
-
+
next_state
-
- The
gen_statem will do a state transition to
- NextStateName
- (which may be the same as the current state),
- set NewData
- and execute all Actions
+
+ The gen_statem does a state transition to
+ NextStateName
+ (which can be the same as the current state),
+ sets NewData ,
+ and executes all Actions .
+
All these terms are tuples or atoms and this property
- will hold in any future version of gen_statem ,
- just in case you need such a promise.
+ will hold in any future version of gen_statem .
-
+
next_state
-
- The
gen_statem will do a state transition to
- NextState
- (which may be the same as the current state),
- set NewData
- and execute all Actions
+
+ The gen_statem does a state transition to
+ NextState
+ (which can be the same as the current state),
+ sets NewData ,
+ and executes all Actions .
+
All these terms are tuples or atoms and this property
- will hold in any future version of gen_statem ,
- just in case you need such a promise.
+ will hold in any future version of gen_statem .
-
+
stop
-
- Terminate the
gen_statem by calling
-
- Module:terminate/3
-
- with Reason and
- NewData , if given.
+
+ Terminates the gen_statem by calling
+
+ Module:terminate/3
+
+ with Reason and
+ NewData , if specified.
+
stop_and_reply
-
- Send all
Replies
- then terminate the gen_statem by calling
-
- Module:terminate/3
-
- with Reason and
- NewData , if given.
+
+ Sends all Replies ,
+ then terminates the gen_statem by calling
+
+ Module:terminate/3
+
+ with Reason and
+ NewData , if specified.
+
keep_state
-
- The
gen_statem will keep the current state, or
- do a state transition to the current state if you like,
- set NewData
- and execute all Actions .
- This is the same as
- {next_state,CurrentState,NewData ,Actions } .
+
+ The gen_statem keeps the current state, or
+ does a state transition to the current state if you like,
+ sets NewData ,
+ and executes all Actions .
+ This is the same as
+ {next_state,CurrentState,NewData ,Actions } .
+
keep_state_and_data
-
- The
gen_statem will keep the current state or
- do a state transition to the current state if you like,
- keep the current server data,
- and execute all Actions .
- This is the same as
- {next_state,CurrentState,CurrentData,Actions } .
+
+ The gen_statem keeps the current state or
+ does a state transition to the current state if you like,
+ keeps the current server data,
+ and executes all Actions .
+ This is the same as
+ {next_state,CurrentState,CurrentData,Actions } .
+
All these terms are tuples or atoms and this property
- will hold in any future version of gen_statem ,
- just in case you need such a promise.
+ will hold in any future version of gen_statem .
-
-
-
-
- Create a linked gen_statem process
-
-
- Creates a gen_statem process according
- to OTP design principles
- (using
- proc_lib
- primitives)
- that is linked to the calling process.
- This is essential when the gen_statem shall be part of
- a supervision tree so it gets linked to its supervisor.
-
-
- The gen_statem process calls
- Module:init/1
- to initialize the server. To ensure a synchronized start-up
- procedure, start_link/3,4 does not return until
- Module:init/1
- has returned.
-
-
- ServerName specifies the
-
- server_name()
-
- to register for the gen_statem .
- If the gen_statem is started with start_link/3
- no ServerName is provided and
- the gen_statem is not registered.
-
- Module is the name of the callback module.
-
- Args is an arbitrary term which is passed as
- the argument to
- Module:init/1 .
-
-
- If the option {timeout,Time} is present in
- Opts , the gen_statem
- is allowed to spend Time milliseconds initializing
- or it will be terminated and the start function will return
- {error,timeout} .
-
-
- If the option
- {debug,Dbgs}
- is present in Opts , debugging through
- sys is activated.
-
-
- If the option {spawn_opt,SpawnOpts} is present in
- Opts , SpawnOpts will be passed
- as option list to
- spawn_opt/2
- which is used to spawn the gen_statem process.
-
-
-
- Using the spawn option monitor is currently not
- allowed, but will cause this function to fail with reason
- badarg .
-
-
-
- If the gen_statem is successfully created
- and initialized this function returns
-
- {ok,Pid} ,
-
- where Pid is the pid()
- of the gen_statem .
- If there already exists a process with the specified
- ServerName this function returns
- {error,{already_started,Pid}} ,
- where Pid is the pid() of that process.
-
-
- If Module:init/1 fails with Reason ,
- this function returns
- {error,Reason} .
- If Module:init/1 returns
-
- {stop,Reason}
-
- or
- ignore ,
- the process is terminated and this function
- returns
-
- {error,Reason}
-
- or
- ignore ,
- respectively.
-
-
-
-
-
-
-
-
- Create a stand-alone gen_statem process
-
-
- Creates a stand-alone gen_statem process according to
- OTP design principles (using
- proc_lib
- primitives).
- Since it does not get linked to the calling process
- this start function can not be used by a supervisor
- to start a child.
-
-
- See start_link/3,4
- for a description of arguments and return values.
-
-
-
-
-
-
- Synchronously stop a generic server
-
-
- The same as
-
- stop(ServerRef , normal, infinity) .
-
-
-
-
-
-
- Synchronously stop a generic server
-
-
- Orders the gen_statem
-
- ServerRef
-
- to exit with the given Reason
- and waits for it to terminate.
- The gen_statem will call
-
- Module:terminate/3
-
- before exiting.
-
-
- This function returns ok if the server terminates
- with the expected reason. Any other reason than normal ,
- shutdown , or {shutdown,Term} will cause an
- error report to be issued through
-
- error_logger:format/2 .
-
- The default Reason is normal .
-
-
- Timeout is an integer greater than zero
- which specifies how many milliseconds to wait for the server to
- terminate, or the atom infinity to wait indefinitely.
- The default value is infinity .
- If the server has not terminated within the specified time,
- a timeout exception is raised.
-
-
- If the process does not exist, a noproc exception
- is raised.
-
-
-
-
-
-
- Make a synchronous call to a gen_statem
+
+
+ Make a synchronous call to a gen_statem .
Makes a synchronous call to the gen_statem
@@ -1093,7 +970,7 @@ handle_event(_, _, State, Data) ->
by sending a request
and waiting until its reply arrives.
- The gen_statem will call the
+ The gen_statem calls the
state function with
event_type()
{call,From} and event content
@@ -1108,33 +985,33 @@ handle_event(_, _, State, Data) ->
and that Reply becomes the return value
of this function.
-
- Timeout is an integer greater than zero
+
+ Timeout is an integer > 0,
which specifies how many milliseconds to wait for a reply,
or the atom infinity to wait indefinitely,
- which is the default. If no reply is received within
+ which is the default. If no reply is received within
the specified time, the function call fails.
To avoid getting a late reply in the caller's
- inbox this function spawns a proxy process that
- does the call. A late reply gets delivered to the
- dead proxy process hence gets discarded. This is
+ inbox, this function spawns a proxy process that
+ does the call. A late reply gets delivered to the
+ dead proxy process, hence gets discarded. This is
less efficient than using
Timeout =:= infinity .
-
- The call may fail for example if the gen_statem dies
+
+ The call can fail, for example, if the gen_statem dies
before or during this function call.
-
- Send an asynchronous event to a gen_statem
+
+ Send an asynchronous event to a gen_statem .
Sends an asynchronous event to the gen_statem
@@ -1144,7 +1021,7 @@ handle_event(_, _, State, Data) ->
and returns ok immediately,
ignoring if the destination node or gen_statem
does not exist.
- The gen_statem will call the
+ The gen_statem calls the
state function with
event_type()
cast and event content
@@ -1154,44 +1031,8 @@ handle_event(_, _, State, Data) ->
-
-
- Reply to a caller
-
-
- This function can be used by a gen_statem
- to explicitly send a reply to a process that waits in
- call/2
- when the reply cannot be defined in
- the return value of a
- state function .
-
-
- From must be the term from the
-
- {call,From }
-
- argument to the
- state function .
- From and Reply
- can also be specified using a
-
- reply_action()
-
- and multiple replies with a list of them.
-
-
-
- A reply sent with this function will not be visible
- in sys debug output.
-
-
-
-
-
-
-
- Enter the gen_statem receive loop
+
+ Enter the gen_statem receive loop.
The same as
@@ -1204,12 +1045,13 @@ handle_event(_, _, State, Data) ->
+
-
- Enter the gen_statem receive loop
+
+ Enter the gen_statem receive loop.
- If Server_or_Actions is a list()
+ If Server_or_Actions is a list() ,
the same as
enter_loop/7
except that no
@@ -1228,34 +1070,35 @@ handle_event(_, _, State, Data) ->
+
-
- Enter the gen_statem receive loop
+
+ Enter the gen_statem receive loop.
-
- Makes an the calling process become a gen_statem .
- Does not return, instead the calling process will enter
- the gen_statem receive loop and become
+
+ Makes the calling process become a gen_statem .
+ Does not return, instead the calling process enters
+ the gen_statem receive loop and becomes
a gen_statem server.
The process must have been started
using one of the start functions in
- proc_lib .
+ proc_lib .
The user is responsible for any initialization of the process,
including registering a name for it.
-
+
This function is useful when a more complex initialization
- procedure is needed than
- the gen_statem behaviour provides.
+ procedure is needed than
+ the gen_statem behavior provides.
-
- Module , Opts and
+
+ Module , Opts , and
Server have the same meanings
as when calling
-
- gen_statem:start[_link]/3,4 .
-
- However, the
+
+ start[_link]/3,4
+ .
+ However, the
server_name()
@@ -1263,265 +1106,260 @@ handle_event(_, _, State, Data) ->
before this function is called.
CallbackMode , State ,
- Data and Actions
+ Data , and Actions
have the same meanings as in the return value of
- Module:init/1 .
- Also, the callback module Module
+ Module:init/1 .
+ Also, the callback module Module
does not need to export an init/1 function.
- Failure: If the calling process was not started by a
- proc_lib
+ The function fails if the calling process was not started by a
+ proc_lib
start function, or if it is not registered
- according to
+ according to
server_name() .
-
-
-
-
-
- CALLBACK FUNCTIONS
-
- The following functions should be exported from a
- gen_statem callback module.
-
-
-
-
- Module:init(Args) -> Result
- Initialize process and internal state
-
- Args = term()
- Result = {CallbackMode,State,Data}
- | {CallbackMode,State,Data,Actions}
- | {stop,Reason} | ignore
-
- CallbackMode =
- callback_mode()
-
- State = state()
-
- Data = data()
-
-
- Actions =
- [action() ] |
- action()
-
- Reason = term()
-
+
+
+ Reply to a caller.
-
- Whenever a gen_statem is started using
- start_link/3,4
- or
- start/3,4 ,
- this function is called by the new process to initialize
- the implementation state and server data.
-
-
- Args is the Args argument provided to the start
- function.
-
-
- If the initialization is successful, the function should
- return {CallbackMode,State,Data} or
- {CallbackMode,State,Data,Actions} .
- CallbackMode selects the
- callback mode .
- of the gen_statem .
- State is the initial
- state()
- and Data the initial server
- data() .
+ This function can be used by a gen_statem
+ to explicitly send a reply to a process that waits in
+ call/2
+ when the reply cannot be defined in
+ the return value of a
+ state function .
- The Actions
- are executed when entering the first
- state just as for a
+ From must be the term from argument
+
+ {call,From }
+
+ to the
state function .
+ From and Reply
+ can also be specified using a
+
+ reply_action()
+
+ and multiple replies with a list of them.
+
+
+ A reply sent with this function is not visible
+ in sys debug output.
+
+
+
+
+
+
+
+
+ Create a standalone gen_statem process.
+
- If something goes wrong during the initialization
- the function should return {stop,Reason}
- or ignore . See
- start_link/3,4 .
+ Creates a standalone gen_statem process according to
+ OTP design principles (using
+ proc_lib
+ primitives).
+ As it does not get linked to the calling process,
+ this start function cannot be used by a supervisor
+ to start a child.
- This function may use
- throw/1
- to return Result .
+ For a description of arguments and return values, see
+ start_link/3,4 .
- Module:StateName(EventType, EventContent, Data) ->
- StateFunctionResult
-
- Module:handle_event(EventType, EventContent,
- State, Data) -> HandleEventResult
-
- Handle an event
-
-
- EventType =
- event_type()
-
- EventContent = term()
-
- State =
- state()
-
-
- Data = NewData =
- data()
-
-
- StateFunctionResult =
-
- state_function_result()
-
-
-
- HandleEventResult =
-
- handle_event_result()
-
-
-
+
+
+ Create a linked gen_statem process.
- Whenever a gen_statem receives an event from
- call/2 ,
- cast/2 or
- as a normal process message one of these functions is called. If
- callback mode
- is state_functions then Module:StateName/3 is called,
- and if it is handle_event_function
- then Module:handle_event/4 is called.
+ Creates a gen_statem process according
+ to OTP design principles
+ (using
+ proc_lib
+ primitives)
+ that is linked to the calling process.
+ This is essential when the gen_statem must be part of
+ a supervision tree so it gets linked to its supervisor.
- If EventType is
- {call,From}
- the caller is waiting for a reply. The reply can be sent
- from this or from any other
- state function
- by returning with {reply,From,Reply} in
- Actions , in
- Replies
- or by calling
- reply(From, Reply) .
+ The gen_statem process calls
+ Module:init/1
+ to initialize the server. To ensure a synchronized startup
+ procedure, start_link/3,4 does not return until
+ Module:init/1
+ has returned.
- If this function returns with a next state that
- does not match equal (=/= ) to the current state
- all postponed events will be retried in the next state.
+ ServerName specifies the
+
+ server_name()
+
+ to register for the gen_statem .
+ If the gen_statem is started with start_link/3 ,
+ no ServerName is provided and
+ the gen_statem is not registered.
-
- The only difference between StateFunctionResult and
- HandleEventResult is that for StateFunctionResult
- the next state has to be an atom but for HandleEventResult
- there is no restriction on the next state.
+
Module is the name of the callback module.
+
+ Args is an arbitrary term that is passed as
+ the argument to
+ Module:init/1 .
+
+ -
+
+ If option {timeout,Time} is present in
+ Opts , the gen_statem
+ is allowed to spend Time milliseconds initializing
+ or it terminates and the start function returns
+
+ {error,timeout} .
+
+
+ -
+
+ If option
+ {debug,Dbgs}
+ is present in Opts , debugging through
+ sys is activated.
+
+
+ -
+
+ If option {spawn_opt,SpawnOpts} is present in
+ Opts , SpawnOpts is passed
+ as option list to
+ erlang:spawn_opt/2 ,
+ which is used to spawn the gen_statem process.
+
+
+
+
+
+ Using spawn option monitor is not
+ allowed, it cause this function to fail with reason
+ badarg .
+
+
- See action()
- for options that can be set and actions that can be done
- by gen_statem after returning from this function.
+ If the gen_statem is successfully created
+ and initialized, this function returns
+
+ {ok,Pid}
+ ,
+ where Pid is the pid()
+ of the gen_statem .
+ If a process with the specified ServerName
+ exists already, this function returns
+
+ {error,{already_started,Pid}} ,
+ where Pid is the pid() of that process.
- These functions may use
- throw/1 ,
- to return the result.
+ If Module:init/1 fails with Reason ,
+ this function returns
+ {error,Reason} .
+ If Module:init/1 returns
+
+ {stop,Reason}
+
+ or
+ ignore ,
+ the process is terminated and this function
+ returns
+
+ {error,Reason}
+
+ or
+ ignore ,
+ respectively.
- Module:terminate(Reason, State, Data) -> Ignored
- Clean up before termination
-
- Reason = normal | shutdown | {shutdown,term()} | term()
- State = state()
- Data = data()
- Ignored = term()
-
+
+ Synchronously stop a generic server.
-
- This function is called by a gen_statem
- when it is about to terminate. It should be the opposite of
- Module:init/1
- and do any necessary cleaning up. When it returns,
- the gen_statem terminates with Reason . The return
- value is ignored.
-
- Reason is a term denoting the stop reason and
- State
- is the internal state of the gen_statem .
-
-
- Reason depends on why the gen_statem
- is terminating.
- If it is because another callback function has returned a
- stop tuple {stop,Reason} in
- Actions ,
- Reason will have the value specified in that tuple.
- If it is due to a failure, Reason is the error reason.
+
+ The same as
+
+ stop(ServerRef , normal, infinity) .
+
-
- If the gen_statem is part of a supervision tree and is
- ordered by its supervisor to terminate, this function will be
- called with Reason = shutdown if the following
- conditions apply:
-
- -
- the
gen_statem has been set
- to trap exit signals, and
-
- -
- the shutdown strategy as defined in the supervisor's
- child specification is an integer timeout value, not
-
brutal_kill .
-
-
-
- Even if the gen_statem is not
- part of a supervision tree, this function will be called
- if it receives an 'EXIT' message from its parent.
- Reason will be the same as
- in the 'EXIT' message.
+
+
+
+
+
+ Synchronously stop a generic server.
+
+
+ Orders the gen_statem
+
+ ServerRef
+
+ to exit with the specified Reason
+ and waits for it to terminate.
+ The gen_statem calls
+
+ Module:terminate/3
+
+ before exiting.
-
- Otherwise, the gen_statem will be immediately terminated.
+
+ This function returns ok if the server terminates
+ with the expected reason. Any other reason than normal ,
+ shutdown , or {shutdown,Term} causes an
+ error report to be issued through
+
+ error_logger:format/2
+ .
+ The default Reason is normal .
-
- Note that for any other reason than normal ,
- shutdown , or {shutdown,Term}
- the gen_statem is assumed to terminate due to an error
- and an error report is issued using
-
- error_logger:format/2 .
-
+
+ Timeout is an integer > 0,
+ which specifies how many milliseconds to wait for the server to
+ terminate, or the atom infinity to wait indefinitely.
+ Defaults to infinity .
+ If the server does not terminate within the specified time,
+ a timeout exception is raised.
- This function may use
- throw/1
- to return Ignored , which is ignored anyway.
+ If the process does not exist, a noproc exception
+ is raised.
+
+
+
+ Callback Functions
+
+ The following functions are to be exported from a
+ gen_statem callback module.
+
+
+
Module:code_change(OldVsn, OldState, OldData, Extra) ->
Result
- Update the internal state during upgrade/downgrade
+ Update the internal state during upgrade/downgrade.
OldVsn = Vsn | {down,Vsn}
Vsn = term()
@@ -1544,24 +1382,23 @@ handle_event(_, _, State, Data) ->
- This function is called by a gen_statem when it should
- update its internal state during a release upgrade/downgrade,
- that is when the instruction {update,Module,Change,...}
- where Change={advanced,Extra} is given in the
+ This function is called by a gen_statem when it is to
+ update its internal state during a release upgrade/downgrade,
+ that is, when the instruction {update,Module,Change,...} ,
+ where Change={advanced,Extra} , is specified in the
appup
- file. See
-
+ file. For more information, see
+
OTP Design Principles
-
- for more information.
+ .
-
- In the case of an upgrade, OldVsn is Vsn , and
- in the case of a downgrade, OldVsn is
- {down,Vsn} . Vsn is defined by the vsn
- attribute(s) of the old version of the callback module
- Module . If no such attribute is defined, the version
- is the checksum of the BEAM file.
+
+ For an upgrade, OldVsn is Vsn , and
+ for a downgrade, OldVsn is
+ {down,Vsn} . Vsn is defined by the vsn
+ attribute(s) of the old version of the callback module
+ Module . If no such attribute is defined, the version
+ is the checksum of the Beam file.
@@ -1569,14 +1406,14 @@ handle_event(_, _, State, Data) ->
callback mode
- during release upgrade/downgrade, the upgrade is no problem
- since the new code surely knows what callback mode
- it needs, but for a downgrade this function will have to
- know from the Extra argument that comes from the
- appup
+ during release upgrade/downgrade, the upgrade is no problem,
+ as the new code surely knows what callback mode
+ it needs. However, for a downgrade this function must
+ know from argument Extra that comes from the
+ sasl:appup
file what callback mode the old code did use.
- It may also be possible to figure this out
- from the {down,Vsn} argument since Vsn
+ It can also be possible to figure this out
+ from argument {down,Vsn} , as Vsn
in effect defines the old callback module version.
@@ -1584,32 +1421,102 @@ handle_event(_, _, State, Data) ->
OldState and OldData is the internal state
of the gen_statem .
-
- Extra is passed as-is from the {advanced,Extra}
- part of the update instruction.
+
+ Extra is passed "as is" from the {advanced,Extra}
+ part of the update instruction.
-
- If successful, the function shall return the updated
- internal state in an
+
+ If successful, the function must return the updated
+ internal state in an
{NewCallbackMode,NewState,NewData} tuple.
If the function returns Reason , the ongoing
- upgrade will fail and roll back to the old release.
+ upgrade fails and rolls back to the old release.
- This function may use
- throw/1
+ This function can use
+ erlang:throw/1
to return Result or Reason .
+
+ Module:init(Args) -> Result
+ Initialize process and internal state.
+
+ Args = term()
+ Result = {CallbackMode,State,Data}
+ | {CallbackMode,State,Data,Actions}
+ | {stop,Reason} | ignore
+
+ CallbackMode =
+ callback_mode()
+
+ State = state()
+
+ Data = data()
+
+
+ Actions =
+ [action() ] |
+ action()
+
+ Reason = term()
+
+
+
+
+ Whenever a gen_statem is started using
+ start_link/3,4
+ or
+ start/3,4 ,
+ this function is called by the new process to initialize
+ the implementation state and server data.
+
+
+ Args is the Args argument provided to the start
+ function.
+
+
+ If the initialization is successful, the function is to
+ return {CallbackMode,State,Data} or
+ {CallbackMode,State,Data,Actions} .
+ CallbackMode selects the
+
+ callback mode
+ of the gen_statem .
+ State is the initial
+ state()
+ and Data the initial server
+ data() .
+
+
+ The Actions
+ are executed when entering the first
+ state just as for a
+ state function .
+
+
+ If the initialization fails,
+ the function is to return {stop,Reason}
+ or ignore ; see
+ start_link/3,4 .
+
+
+ This function can use
+ erlang:throw/1
+ to return Result .
+
+
+
+
Module:format_status(Opt, [PDict,State,Data]) ->
Status
Optional function for providing a term describing the
- current gen_statem status
+ current gen_statem status.
Opt = normal | terminate
PDict = [{Key, Value}]
@@ -1627,58 +1534,59 @@ handle_event(_, _, State, Data) ->
-
- This callback is optional, so a callback module need not
- export it. The gen_statem module provides a default
- implementation of this function that returns
- {State,Data} . If this callback fails the default
- function will return {State,Info}
+
+ This callback is optional, so a callback module does not need
+ to export it. The gen_statem module provides a default
+ implementation of this function that returns
+ {State,Data} . If this callback fails, the default
+ function returns {State,Info} ,
where Info informs of the crash but no details,
to hide possibly sensitive data.
- This function is called by a gen_statem process when:
+ This function is called by a gen_statem process when
+ any of the following apply:
- -
+
-
One of
-
+
sys:get_status/1,2
- is invoked to get the gen_statem status. Opt is set
- to the atom normal for this case.
+ is invoked to get the gen_statem status. Opt is set
+ to the atom normal for this case.
- -
+
-
The
gen_statem terminates abnormally and logs an error.
Opt is set to the atom terminate for this case.
-
+
- This function is useful for customising the form and
+ This function is useful for changing the form and
appearance of the gen_statem status for these cases. A
- callback module wishing to customise the
+ callback module wishing to change the
sys:get_status/1,2
- return value as well as how
+ return value and how
its status appears in termination error logs exports an
- instance of format_status/2 that returns a term
+ instance of format_status/2 , which returns a term
describing the current status of the gen_statem .
-
- PDict is the current value of the gen_statem 's
- process dictionary.
+
+ PDict is the current value of the process dictionary
+ of the gen_statem .
-
+
State
is the internal state of the gen_statem .
-
+
Data
is the internal server data of the gen_statem .
-
- The function should return Status , a term that
- customises the details of the current state and status of
+
+ The function is to return Status , a term that
+ changes the details of the current state and status of
the gen_statem . There are no restrictions on the
form Status can take, but for the
@@ -1687,38 +1595,197 @@ handle_event(_, _, State, Data) ->
case (when Opt
is normal ), the recommended form for
the Status value is [{data, [{"State",
- Term}]}] where Term provides relevant details of
- the gen_statem state. Following this recommendation isn't
- required, but doing so will make the callback module status
+ Term}]}], where Term provides relevant details of
+ the gen_statem state. Following this recommendation is not
+ required, but it makes the callback module status
consistent with the rest of the
sys:get_status/1,2
return value.
-
+
One use for this function is to return compact alternative
state representations to avoid having large state terms
- printed in logfiles. Another is to hide sensitive data from
+ printed in log files. Another use is to hide sensitive data from
being written to the error log.
- This function may use
- throw/1
+ This function can use
+ erlang:throw/1
to return Status .
+
+ Module:StateName(EventType, EventContent, Data) ->
+ StateFunctionResult
+
+ Module:handle_event(EventType, EventContent,
+ State, Data) -> HandleEventResult
+
+ Handle an event.
+
+
+ EventType =
+ event_type()
+
+ EventContent = term()
+
+ State =
+ state()
+
+
+ Data = NewData =
+ data()
+
+
+ StateFunctionResult =
+
+ state_function_result()
+
+
+
+ HandleEventResult =
+
+ handle_event_result()
+
+
+
+
+
+ Whenever a gen_statem receives an event from
+ call/2 ,
+ cast/2 , or
+ as a normal process message, one of these functions is called. If
+ callback mode
+ is state_functions , Module:StateName/3 is called,
+ and if it is handle_event_function ,
+ Module:handle_event/4 is called.
+
+
+ If EventType is
+ {call,From} ,
+ the caller waits for a reply. The reply can be sent
+ from this or from any other
+ state function
+ by returning with {reply,From,Reply} in
+ Actions , in
+ Replies ,
+ or by calling
+ reply(From, Reply) .
+
+
+ If this function returns with a next state that
+ does not match equal (=/= ) to the current state,
+ all postponed events are retried in the next state.
+
+
+ The only difference between StateFunctionResult and
+ HandleEventResult is that for StateFunctionResult
+ the next state must be an atom, but for HandleEventResult
+ there is no restriction on the next state.
+
+
+ For options that can be set and actions that can be done
+ by gen_statem after returning from this function,
+ see action() .
+
+
+ These functions can use
+ erlang:throw/1 ,
+ to return the result.
+
+
+
+
+
+ Module:terminate(Reason, State, Data) -> Ignored
+ Clean up before termination.
+
+ Reason = normal | shutdown | {shutdown,term()} | term()
+ State = state()
+ Data = data()
+ Ignored = term()
+
+
+
+ This function is called by a gen_statem
+ when it is about to terminate. It is to be the opposite of
+ Module:init/1
+ and do any necessary cleaning up. When it returns,
+ the gen_statem terminates with Reason . The return
+ value is ignored.
+
+ Reason is a term denoting the stop reason and
+ State
+ is the internal state of the gen_statem .
+
+
+ Reason depends on why the gen_statem
+ is terminating.
+ If it is because another callback function has returned, a
+ stop tuple {stop,Reason} in
+ Actions ,
+ Reason has the value specified in that tuple.
+ If it is because of a failure, Reason is the error reason.
+
+
+ If the gen_statem is part of a supervision tree and is
+ ordered by its supervisor to terminate, this function is
+ called with Reason = shutdown if both the following
+ conditions apply:
+
+ -
+
+ The gen_statem has been set
+ to trap exit signals.
+
+
+ -
+
+ The shutdown strategy as defined in the supervisor's
+ child specification is an integer time-out value, not
+ brutal_kill .
+
+
+
+
+ Even if the gen_statem is not
+ part of a supervision tree, this function is called
+ if it receives an 'EXIT' message from its parent.
+ Reason is the same as
+ in the 'EXIT' message.
+
+
+ Otherwise, the gen_statem is immediately terminated.
+
+
+ Notice that for any other reason than normal ,
+ shutdown , or {shutdown,Term} ,
+ the gen_statem is assumed to terminate because of an error
+ and an error report is issued using
+
+ error_logger:format/2 .
+
+
+
+ This function can use
+ erlang:throw/1
+ to return Ignored , which is ignored anyway.
+
+
+
- SEE ALSO
+ See Also
gen_event(3) ,
gen_fsm(3) ,
gen_server(3) ,
- supervisor(3) ,
proc_lib(3) ,
+ supervisor(3) ,
sys(3)
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index ca0fce55e2..8b0fbed7c0 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -5,7 +5,7 @@
2016
- Ericsson AB. All Rights Reserved.
+ Ericsson AB. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +22,7 @@
- gen_statem Behaviour
+ gen_statem Behavior
@@ -33,63 +33,60 @@
This section is to be read with the
gen_statem(3)
- manual page in STDLIB, where all interface functions and callback
+ manual page in STDLIB , where all interface functions and callback
functions are described in detail.
-
- This is a new behaviour in OTP-19.0.
- It has been thoroughly reviewed, is stable enough
- to be used by at least two heavy OTP applications, and is here to stay.
- But depending on user feedback, we do not expect
- but might find it necessary to make minor
- not backwards compatible changes into OTP-20.0,
- so its state can be designated as "not quite experimental"...
-
+
+
+ This is a new behavior in Erlang/OTP 19.0.
+ It has been thoroughly reviewed, is stable enough
+ to be used by at least two heavy OTP applications, and is here to stay.
+ Depending on user feedback, we do not expect
+ but can find it necessary to make minor
+ not backward compatible changes into Erlang/OTP 20.0.
+
+
- Event Driven State Machines
+ Event-Driven State Machines
Established Automata theory does not deal much with
how a state transition is triggered,
- but in general assumes that the output is a function
+ but assumes that the output is a function
of the input (and the state) and that they are
some kind of values.
- For an Event Driven State Machine the input is an event
+ For an Event-Driven State Machine, the input is an event
that triggers a state transition and the output
is actions executed during the state transition.
It can analogously to the mathematical model of a
- Finite State Machine be described as
- a set of relations of the form:
+ Finite-State Machine be described as
+ a set of relations of the following form:
State(S) x Event(E) -> Actions(A), State(S')
- These relations are interpreted as meaning:
-
- If we are in state S and event E occurs, we
+
These relations are interpreted as follows:
+ if we are in state S and event E occurs, we
are to perform actions A and make a transition to
- state S' .
-
-
- Note that S' may be equal to S .
+ state S' . Notice that S' can be equal to S .
- Since A and S' depend only on
- S and E the kind of state machine described
- here is a Mealy Machine.
- (See for example the corresponding Wikipedia article)
+ As A and S' depend only on
+ S and E , the kind of state machine described
+ here is a Mealy Machine
+ (see, for example, the corresponding Wikipedia article).
- Like most gen_ behaviours, gen_statem keeps
- a server Data besides the state. This and the fact that
+ Like most gen_ behaviors, gen_statem keeps
+ a server Data besides the state. Because of this, and as
there is no restriction on the number of states
- (assuming enough virtual machine memory)
- or on the number of distinct input events actually makes
- a state machine implemented with this behaviour Turing complete.
- But it feels mostly like an Event Driven Mealy Machine.
+ (assuming that there is enough virtual machine memory)
+ or on the number of distinct input events, makes
+ a state machine implemented with this behavior Turing complete.
+ But it feels mostly like an Event-Driven Mealy Machine.
@@ -99,38 +96,40 @@ State(S) x Event(E) -> Actions(A), State(S')
Callback Modes
- The gen_statem behaviour supports two different callback modes.
- In the mode
-
- state_functions ,
-
- the state transition rules are written as a number of Erlang
- functions, which conform to the following convention:
+ The gen_statem behavior supports two callback modes:
-
+
+ -
+
+ In mode
+ state_functions ,
+ the state transition rules are written as some Erlang
+ functions, which conform to the following convention:
+
+
StateName(EventType, EventContent, Data) ->
.. code for actions here ...
{next_state, NewStateName, NewData}.
-
- In the mode
-
- handle_event_function
-
- there is only one
- Erlang function that implements all state transition rules:
-
-
+
+ -
+
+ In mode
+ handle_event_function ,
+ only one Erlang function provides all state transition rules:
+
+
handle_event(EventType, EventContent, State, Data) ->
.. code for actions here ...
{next_state, State', Data'}
+
+
- Both these modes allow other return tuples
- that you can find in the
+ Both these modes allow other return tuples; see
- reference manual.
-
- These other return tuples can for example stop the machine,
- execute state transition actions on the machine engine itself
+ Module:StateName/3
+ in the gen_statem manual page.
+ These other return tuples can, for example, stop the machine,
+ execute state transition actions on the machine engine itself,
and send replies.
@@ -139,54 +138,55 @@ handle_event(EventType, EventContent, State, Data) ->
The two
callback modes
- gives different possibilities
+ give different possibilities
and restrictions, but one goal remains:
you want to handle all possible combinations of
events and states.
- You can for example do this by focusing on one state at the time
- and for every state ensure that all events are handled,
- or the other way around focus on one event at the time
- and ensure that it is handled in every state,
- or mix these strategies.
+ This can be done, for example, by focusing on one state at the time
+ and for every state ensure that all events are handled.
+ Alternatively, you can focus on one event at the time
+ and ensure that it is handled in every state.
+ You can also use a mix of these strategies.
- With state_functions you are restricted to use
- atom only states, and the gen_statem engine dispatches
- on state name for you. This encourages the callback module
+ With state_functions , you are restricted to use
+ atom-only states, and the gen_statem engine dispatches
+ on state name for you. This encourages the callback module
to gather the implementation of all event actions particular
- to one state in the same place in the code
+ to one state in the same place in the code,
hence to focus on one state at the time.
- This mode fits well when you have a regular state diagram
- like the ones in this chapter that describes all events and actions
+ This mode fits well when you have a regular state diagram,
+ like the ones in this chapter, which describes all events and actions
belonging to a state visually around that state,
and each state has its unique name.
- With handle_event_function you are free to mix strategies
- as you like because all events and states
- are handled in the the same callback function.
+ With handle_event_function , you are free to mix strategies,
+ as all events and states are handled in the same callback function.
This mode works equally well when you want to focus on
- one event at the time or when you want to focus on
- one state at the time, but the handle_event/4 function
+ one event at the time or on
+ one state at the time, but function
+
+ Module:handle_event/4
quickly grows too large to handle without introducing dispatching.
- The mode enables the use of non-atom states for example
- complex states or even hiearchical states.
+ The mode enables the use of non-atom states, for example,
+ complex states or even hierarchical states.
If, for example, a state diagram is largely alike
- for the client and for the server side of a protocol;
- then you can have a state {StateName,server} or
- {StateName,client} and since you do the dispatching
- yourself you make StateName decide where in the code
+ for the client side and the server side of a protocol,
+ you can have a state {StateName,server} or
+ {StateName,client} . Also, as you do the dispatching
+ yourself, you make StateName decide where in the code
to handle most events in the state.
- The second element of the tuple is then used to select
- whether to handle special client side or server side events.
+ The second element of the tuple is then used to select
+ whether to handle special client-side or server-side events.
- This is an example starting off as equivalent to the the example in the
-
- A door with a code lock can be viewed as a state machine. - Initially, the door is locked. Anytime someone presses a button, - this generates an event. + A door with a code lock can be seen as a state machine. + Initially, the door is locked. When someone presses a button, + an event is generated. Depending on what buttons have been pressed before, the sequence so far can be correct, incomplete, or wrong. -
-- If it is correct, the door is unlocked for 10 seconds (10000 ms). - If it is incomplete, we wait for another button to be pressed. If - it is is wrong, we start all over, - waiting for a new button sequence. + If correct, the door is unlocked for 10 seconds (10,000 milliseconds). + If incomplete, we wait for another button to be pressed. If + wrong, we start all over, waiting for a new button sequence.
- We can implement such a code lock state machine using
+ This code lock state machine can be implemented using
- In the example in the previous section, the
gen_statem:start_link({local,?NAME}, ?MODULE, Code, []).
]]>
-
- The first argument,
- If the name is omitted, the
- The second argument,
- The interface functions (
- The third argument,
- The fourth argument,
If name registration succeeds, the new
+
Function
+ Function
The function notifying the code lock about a button event is
implemented using
The first argument is the name of the
The event is made into a message and sent to the
If the door is locked and a button is pressed, the pressed
- button is compared with the next correct button and,
- depending on the result, the door is either unlocked
+ button is compared with the next correct button.
+ Depending on the result, the door is either unlocked
and the
- If the pressed button is incorrect the server data
+ If the pressed button is incorrect, the server data
restarts from the start of the code sequence.
- In state
- 10000 is a time-out value in milliseconds.
- After this time, that is; 10 seconds, a time-out occurs.
+ 10,000 is a time-out value in milliseconds.
+ After this time (10 seconds), a time-out occurs.
Then,
@@ -496,16 +494,16 @@ open(timeout, _, Data) ->
All State Events
- Sometimes an event can arrive in any state of the gen_statem .
+ Sometimes events can arrive in any state of the gen_statem .
It is convenient to handle these in a common state handler function
that all state functions call for events not specific to the state.
- Let's introduce a code_length/0 function that returns
+ Consider a code_length/0 function that returns
the length of the correct code
- (that should not be sensitive to reveal...).
- We'll dispatch all events that are not state specific
- to the common function handle_event/3 .
+ (that should not be sensitive to reveal).
+ We dispatch all events that are not state-specific
+ to the common function handle_event/3 :
This example uses
gen_statem:call/2
-
+ ,
which waits for a reply from the server.
The reply is sent with a {reply,From,Reply} tuple
in an action list in the {keep_state,...} tuple
@@ -545,9 +543,12 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->
One Event Handler
- If you use the mode handle_event_function
- all events are handled in handle_event/4 and we
- may (but do not have to) use an event-centered approach
+ If mode handle_event_function is used,
+ all events are handled in
+
+ Module:handle_event/4
+
+ and we can (but do not have to) use an event-centered approach
where we dispatch on event first and then state:
The gen_statem is automatically terminated by its supervisor.
Exactly how this is done is defined by a
shutdown strategy
- set in the supervisor.
+ set in the supervisor.
If it is necessary to clean up before termination, the shutdown
- strategy must be a time-out value and the gen_statem must
- in the init/1 function set itself to trap exit signals
+ strategy must be a time-out value and the gen_statem must
+ in function init/1 set itself to trap exit signals
by calling
- process_flag(trap_exit, true) .
-
- When ordered to shutdown, the gen_statem then calls
- the callback function
- terminate(shutdown, State, Data) :
+ process_flag(trap_exit, true)
+ .
+ When ordered to shut down, the gen_statem then calls
+ callback function terminate(shutdown, State, Data) :
@@ -617,9 +617,9 @@ init(Args) ->
...
]]>
- In this example we let the terminate/3 function
- lock the door if it is open so we do not accidentally leave the door
- open when the supervision tree terminates.
+ In the following example, function terminate/3
+ locks the door if it is open, so we do not accidentally leave the door
+ open when the supervision tree terminates:
@@ -634,8 +634,8 @@ terminate(_Reason, State, _Data) ->
If the gen_statem is not part of a supervision tree,
it can be stopped using
- gen_statem:stop ,
-
+ gen_statem:stop
+ ,
preferably through an API function:
gen_statem:stop(?NAME).
]]>
- This makes the gen_statem call the terminate/3
- callback function just like for a supervised server
+ This makes the gen_statem call callback function
+ terminate/3 just like for a supervised server
and waits for the process to terminate.
@@ -659,15 +659,15 @@ stop() ->
Actions
- In the first chapters we mentioned actions as a part of
- the general state machine model, and these actions
- are implemented with the code the gen_statem
- callback module executes in an event handling
+ In the first sections actions were mentioned as a part of
+ the general state machine model. These actions
+ are implemented with the code that callback module
+ gen_statem executes in an event-handling
callback function before returning
to the gen_statem engine.
- There are more specific state transition actions
+ There are more specific state-transition actions
that a callback function can order the gen_statem
engine to do after the callback function return.
These are ordered by returning a list of
@@ -680,27 +680,28 @@ stop() ->
from the
- callback function.
-
+ callback function
+ .
These state transition actions affect the gen_statem
- engine itself. They can:
+ engine itself and can do the following:
- - Postpone the current event.
- - Hibernate the
gen_statem .
- - Start an event timeout.
- - Reply to a caller.
- - Generate the next event to handle.
+ - Postpone the current event
+ - Hibernate the
gen_statem
+ - Start an event time-out
+ - Reply to a caller
+ - Generate the next event to handle
- We have mentioned the event timeout
- and replying to a caller in the example above.
- An example of event postponing comes in later in this chapter.
- See the
+ In the example earlier was mentioned the event time-out
+ and replying to a caller.
+ An example of event postponing is included later in this chapter.
+ For details, see the
- reference manual
+ gen_statem(3)
- for details. You can for example actually reply to several callers
+ manual page.
+ You can, for example, reply to many callers
and generate multiple next events to handle.
@@ -710,16 +711,16 @@ stop() ->
Event Types
- So far we have mentioned a few
+ The previous sections mentioned a few
- event types.
-
+ event types
+ .
Events of all types are handled in the same callback function,
for a given state, and the function gets
EventType and EventContent as arguments.
- Here is the complete list of event types and where
+ The following is a complete list of event types and where
they come from:
@@ -735,13 +736,13 @@ stop() ->
Generated by
gen_statem:call
-
+ ,
where From is the reply address to use
when replying either through the state transition action
{reply,From,Msg} or by calling
- gen_statem:reply .
-
+ gen_statem:reply
+ .
info
-
@@ -750,15 +751,15 @@ stop() ->
timeout
-
- Generated by the state transition action
+ Generated by state transition action
{timeout,Time,EventContent} (or its short form Time )
timer timing out.
internal
-
- Generated by the state transition action
+ Generated by state transition action
{next_event,internal,EventContent} .
- In fact all event types above can be generated using
+ All event types above can be generated using
{next_event,EventType,EventContent} .
@@ -767,34 +768,34 @@ stop() ->
- State Timeouts
+ State Time-Outs
- The timeout event generated by the state transition action
- {timeout,Time,EventContent} is an event timeout,
- that is; if an event arrives the timer is cancelled.
- You get either an event or a timeout but not both.
+ The time-out event generated by state transition action
+ {timeout,Time,EventContent} is an event time-out,
+ that is, if an event arrives the timer is cancelled.
+ You get either an event or a time-out, but not both.
- Often you want a timer to not be cancelled by any event
+ Often you want a timer not to be cancelled by any event
or you want to start a timer in one state and respond
- to the timeout in another. This can be accomplished
- with a regular erlang timer:
+ to the time-out in another. This can be accomplished
+ with a regular Erlang timer:
erlang:start_timer .
- Looking at the example in this chapter so far; using the
+ For the example so far in this chapter: using the
gen_statem event timer has the consequence that
if a button event is generated while in the open state,
- the timeout is cancelled and the button event is delivered.
- Therefore we chose to lock the door if this happended.
+ the time-out is cancelled and the button event is delivered.
+ So, we choose to lock the door if this occurred.
- Suppose we do not want a button to lock the door,
+ Suppose that we do not want a button to lock the door,
instead we want to ignore button events in the open state.
Then we start a timer when entering the open state
- and wait for it to expire while ignoring button events:
+ and waits for it to expire while ignoring button events:
...
]]>
- If you need to cancel a timer due to some other event you can use
+ If you need to cancel a timer because of some other event, you can use
- erlang:cancel_timer(Tref) .
-
- Note that a timeout message can not arrive after this,
+ erlang:cancel_timer(Tref)
+ .
+ Notice that a time-out message cannot arrive after this,
unless you have postponed it (see the next section) before,
- so make sure you do not accidentally postpone such messages.
+ so ensure that you do not accidentally postpone such messages.
- Another way to cancel a timer is to not cancel it,
- but instead to ignore it if it arrives in a state
+ Another way to cancel a timer is not to cancel it,
+ but to ignore it if it arrives in a state
where it is known to be late.
@@ -839,7 +840,7 @@ open(cast, {button,_}, Data) ->
If you want to ignore a particular event in the current state
and handle it in a future state, you can postpone the event.
A postponed event is retried after the state has
- changed i.e OldState =/= NewState .
+ changed, that is, OldState =/= NewState .
Postponing is ordered by the state transition
@@ -850,8 +851,8 @@ open(cast, {button,_}, Data) ->
In this example, instead of ignoring button events
- while in the open state we can postpone them
- and they will be queued and later handled in the locked state:
+ while in the open state, we can postpone them
+ and they are queued and later handled in the locked state:
...
]]>
- The fact that a postponed event is only retried after a state change
- translates into a requirement on the event and state space:
- if you have a choice between storing a state data item
- in the State or in the Data ;
- should a change in the item value affect which events that
- are handled, then this item ought to be part of the state.
+ A postponed event is only retried after a state change
+ translates into a requirement on the event and state space.
+ If you have a choice between storing a state data item
+ in the State or in the Data :
+ if a change in the item value affects which events that
+ are handled, then this item is to be part of the state.
- What you want to avoid is that you maybe much later decide
- to postpone an event in one state and by misfortune it is never retried
- because the code only changes the Data but not the State .
+ You want to avoid that you maybe much later decide
+ to postpone an event in one state and by misfortune it is never retried,
+ as the code only changes the Data but not the State .
@@ -883,7 +884,7 @@ open(cast, {button,_}, Data) ->
or from the context.
- Possible actions may be; ignore as in drop the event
+ Possible actions: ignore as in drop the event
(maybe log it) or deal with the event in some other state
as in postpone it.
@@ -892,10 +893,10 @@ open(cast, {button,_}, Data) ->
Selective Receive
- Erlang's selective receive statement is often used to
- describe simple state machine examples in straightforward
- Erlang code. Here is a possible implementation of
- the first example:
+ Erlang's selective receive statement is often used to
+ describe simple state machine examples in straightforward
+ Erlang code. The following is a possible implementation of
+ the first example:
io:format("Open~n", []).
]]>
- The selective receive in this case causes open
- to implicitly postpone any events to the locked state.
+ The selective receive in this case causes implicitly open
+ to postpone any events to the locked state.
- A selective receive can not be used from a gen_statem
- behaviour just as for any gen_* behavior
- since the receive statement is within the gen_* engine itself.
- It has to be there because all
+ A selective receive cannot be used from a gen_statem
+ behavior as for any gen_* behavior,
+ as the receive statement is within the gen_* engine itself.
+ It must be there because all
sys
- compatible behaviours must respond to system messages and therefore
+ compatible behaviors must respond to system messages and therefore
do that in their engine receive loop,
passing non-system messages to the callback module.
@@ -955,15 +956,15 @@ do_unlock() ->
action
- postpone is designed to be able to model
- selective receives. A selective receive implicitly postpones
+ postpone is designed to model
+ selective receives. A selective receive implicitly postpones
any not received events, but the postpone
state transition action explicitly postpones one received event.
- Other than that both mechanisms have got the same theoretical
+ Both mechanisms have the same theoretical
time and memory complexity, while the selective receive
- language construct has got smaller constant factors.
+ language construct has smaller constant factors.
@@ -971,9 +972,9 @@ do_unlock() ->
- Self Generated Events
+ Self-Generated Events
- It may be beneficial in some cases to be able to generate events
+ It can sometimes be beneficial to be able to generate events
to your own state machine.
This can be done with the state transition
@@ -984,30 +985,30 @@ do_unlock() ->
You can generate events of any existing
- type,
-
- but the internal type can only be generated through the
- next_event action and hence can not come from an external source,
+ type
+
,
+ but the internal type can only be generated through action
+ next_event . Hence, it cannot come from an external source,
so you can be certain that an internal event is an event
from your state machine to itself.
- One example of using self generated events may be when you have
+ One example of using self-generated events can be when you have
a state machine specification that uses state entry actions.
- That you could code using a dedicated function
- to do the state transition. But if you want that code to be
- visible besides the other state logic you can insert
+ You can code that using a dedicated function
+ to do the state transition. But if you want that code to be
+ visible besides the other state logic, you can insert
an internal event that does the entry actions.
This has the same unfortunate consequence as using
- state transition functions that everywhere you go to
- the state in question you will have to explicitly
+ state transition functions: everywhere you go to
+ the state, you must explicitly
insert the internal event
- or use state transition function.
+ or use a state transition function.
- Here is an implementation of entry actions
+ The following is an implementation of entry actions
using internal events with content enter
- utilizing a helper function enter/3 for state entry:
+ using a helper function enter/3 for state entry:
Example Revisited
- Here is the example after all mentioned modifications
- and some more utilizing the entry actions,
+ This section includes the example after all mentioned modifications
+ and some more using the entry actions,
which deserves a new state diagram:
- Code lock state diagram revisited
+ Code Lock State Diagram Revisited
- Note that this state diagram does not specify how to handle
- a button event in the state open , so you will have to
- read some other place that is here that unspecified events
- shall be ignored as in not consumed but handled in some other state.
- Nor does it show that the code_length/0 call shall be
- handled in every state.
+ Notice that this state diagram does not specify how to handle
+ a button event in the state open . So, you need to
+ read somewhere else that unspecified events
+ must be ignored as in not consumed but handled in some other state.
+ Also, the state diagram does not show that the code_length/0
+ call must be handled in every state.
@@ -1147,10 +1148,11 @@ code_change(_Vsn, State, Data, _Extra) ->
Callback Mode: handle_event_function
- What to change to use one handle_event/4 function.
- Here a clean first-dispatch-on-event approach
- does not work that well due to the generated
- entry actions:
+ This section describes what to change in the example
+ to use one handle_event/4 function.
+ The following clean first-dispatch-on-event approach
+ does not work that well because of the generated
+ entry actions:
]]>
- Note that postponing buttons from the locked state
+ Notice that postponing buttons from the locked state
to the open state feels like the wrong thing to do
for a code lock, but it at least illustrates event postponing.
@@ -1206,33 +1208,33 @@ handle_event({call,From}, code_length, _State, #{code := Code}) ->
Filter the State
- The example servers so far in this chapter will for example
- when killed by an exit signal or due to an internal error
- print out the full internal state in the error log.
+ The example servers so far in this chapter
+ print the full internal state in the error log, for example,
+ when killed by an exit signal or because of an internal error.
This state contains both the code lock code
- and which digits that remains to unlock.
+ and which digits that remain to unlock.
This state data can be regarded as sensitive,
and maybe not what you want in the error log
- because of something unpredictable happening.
+ because of some unpredictable event.
Another reason to filter the state can be
- that the state is too big to print out since it fills
+ that the state is too large to print, as it fills
the error log with uninteresting details.
- To avoid this you can format the internal state
+ To avoid this, you can format the internal state
that gets in the error log and gets returned from
sys:get_status/1,2
- by implementing the
+ by implementing function
Module:format_status/2
-
- function, for example like this:
+ ,
+ for example like this:
Module:format_status/2
- function. If you do not a default implementation is used that
+ function. If you do not, a default implementation is used that
does the same as this example function without filtering
- the Data term that is: StateData = {State,Data} .
+ the Data term, that is, StateData = {State,Data} .
@@ -1275,54 +1277,56 @@ format_status(Opt, [_PDict,State,Data]) ->
handle_event_function
- enables using a non-atom state as described in
+ enables using a non-atom state as described in section
- Callback Modes,
-
- for example a complex state term like a tuple.
+ Callback Modes
+ ,
+ for example, a complex state term like a tuple.
One reason to use this is when you have
- a state item that affects the event handling
- in particular when combining that with postponing events.
- Let us complicate the previous example
+ a state item that affects the event handling,
+ in particular in combination with postponing events.
+ We complicate the previous example
by introducing a configurable lock button
- (this is the state item in question)
- that in the open state immediately locks the door,
+ (this is the state item in question),
+ which in the open state immediately locks the door,
and an API function set_lock_button/1 to set the lock button.
Suppose now that we call set_lock_button
while the door is open,
and have already postponed a button event
- that up until now was not the lock button;
- the sensible thing might be to say that
- the button was pressed too early so it should
- not be recognized as the lock button,
- but then it might be surprising that a button event
+ that until now was not the lock button.
+ The sensible thing can be to say that
+ the button was pressed too early so it is
+ not to be recognized as the lock button.
+ However, then it can be surprising that a button event
that now is the lock button event arrives (as retried postponed)
immediately after the state transits to locked .
- So let us make the button/1 function synchronous
- by using gen_statem:call ,
+ So we make the button/1 function synchronous
+ by using
+
+ gen_statem:call
and still postpone its events in the open state.
Then a call to button/1 during the open
- state will not return until the state transits to locked
- since it is there the event is handled and the reply is sent.
+ state does not return until the state transits to locked ,
+ as it is there the event is handled and the reply is sent.
- If now one process calls set_lock_button/1
- to change the lock button while some other process
- hangs in button/1 with the new lock button
- it could be expected that the hanging lock button call
+ If a process now calls set_lock_button/1
+ to change the lock button while another process
+ hangs in button/1 with the new lock button,
+ it can be expected that the hanging lock button call
immediately takes effect and locks the lock.
- Therefore we make the current lock button a part of the state
- so when we change the lock button the state will change
- and all postponed events will be retried.
+ Therefore, we make the current lock button a part of the state,
+ so that when we change the lock button, the state changes
+ and all postponed events are retried.
- We define the state as {StateName,LockButton}
+ We define the state as {StateName,LockButton} ,
where StateName is as before
and LockButton is the current lock button:
@@ -1441,10 +1445,9 @@ format_status(Opt, [_PDict,State,Data]) ->
end.
]]>
- It may be an ill-fitting model for a physical code lock
- that the button/1 call might hang until the lock
- is locked. But for an API in general it is really not
- that strange.
+ It can be an ill-fitting model for a physical code lock
+ that the button/1 call can hang until the lock
+ is locked. But for an API in general it is not that strange.
@@ -1457,26 +1460,25 @@ format_status(Opt, [_PDict,State,Data]) ->
and they have some state(s) in their lifetime in which
the servers can be expected to idle for a while,
and the amount of heap memory all these servers need
- is a problem; then it is possible to minimize
- the memory footprint of a server by hibernating it through
+ is a problem, then the memory footprint of a server
+ can be mimimized by hibernating it through
proc_lib:hibernate/3 .
- To hibernate a process is rather costly. See
-
- erlang:hibernate/3 .
-
- It is in general not something you want to do
- after every event.
+ It is rather costly to hibernate a process; see
+
+ erlang:hibernate/3
+ .
+ It is not something you want to do after every event.
- We can in this example hibernate in the {open,_} state
- since what normally happens in that state is that
- the state timeout after a while
+ We can in this example hibernate in the {open,_} state,
+ because what normally occurs in that state is that
+ the state time-out after a while
triggers a transition to {locked,_} :
action list on the last line
when entering the {open,_} state is the only change.
- If any event arrives in the {open,_}, state we
- do not bother to re-hibernate, so the server stays
+ If any event arrives in the {open,_}, state, we
+ do not bother to rehibernate, so the server stays
awake after any event.
To change that we would need to insert
- the hibernate action in more places,
- for example for the state independent set_lock_button
+ action hibernate in more places.
+ For example, for the state-independent set_lock_button
and code_length operations that then would have to
be aware of using hibernate while in the
- {open,_} state which would clutter the code.
+ {open,_} state, which would clutter the code.
- This server probably does not use an amount of
+ This server probably does not use
heap memory worth hibernating for.
- To gain anything from hibernation your server would
- have to actually produce some garbage during callback execution,
- for which this example server may serve as a bad example.
+ To gain anything from hibernation, your server would
+ have to produce some garbage during callback execution,
+ for which this example server can serve as a bad example.
--
cgit v1.2.3
From 17405463ba134e71ff09e8d2921de9aa931805ee Mon Sep 17 00:00:00 2001
From: Raimo Niskanen
Date: Mon, 9 May 2016 14:20:03 +0200
Subject: Fix all seealso and other minor changes
---
lib/stdlib/doc/src/gen_statem.xml | 296 ++++++++++----------------------
system/doc/design_principles/statem.xml | 184 ++++++--------------
2 files changed, 145 insertions(+), 335 deletions(-)
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index be90488a1f..b1d9799917 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -84,9 +84,7 @@
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
- .
+ OTP Design Principles .
A gen_statem assumes all specific parts to be located in a
@@ -136,28 +134,23 @@ erlang:'!' -----> Module:StateName/3
callback mode
is state_functions , the state must be an atom and
is used as the state function name; see
-
- Module:StateName/3
- .
+ Module:StateName/3 .
This gathers all code for a specific state
in one function and hence dispatches on state first.
Notice that in this mode
the mandatory callback function
-
- Module:terminate/3
- makes the state name terminate unusable.
+ Module:terminate/3
+ makes the state name terminate unusable.
When the
callback mode
is handle_event_function , the state can be any term
and the state function name is
-
- Module:handle_event/4
- .
+ Module:handle_event/4 .
This makes it easy to dispatch on state or on event as you desire.
Be careful about which events you handle in which
- states so that you do not accidentally postpone one event
+ states so that you do not accidentally postpone an event
forever creating an infinite busy loop.
@@ -178,15 +171,12 @@ erlang:'!' -----> Module:StateName/3
The state function
can insert events using the
-
- action() next_event
-
+ action()
+ next_event
and such an event is inserted as the next to present
to the state function. That is, as if it is
the oldest incoming event. A dedicated
-
- event_type()
-
+ event_type()
internal can be used for such events making them impossible
to mistake for external events.
@@ -206,21 +196,18 @@ erlang:'!' -----> Module:StateName/3
For the details of a state transition, see type
-
- transition_option()
- .
+ transition_option() .
A gen_statem handles system messages as described in
sys .
- The sys module can be used for debugging a gen_statem .
+ 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) .
+ process_flag(trap_exit, true) .
Unless otherwise stated, all functions in this module fail if
@@ -229,9 +216,7 @@ erlang:'!' -----> Module:StateName/3
The gen_statem process can go into hibernation; see
-
- proc_lib:hibernate/3
- .
+ proc_lib:hibernate/3 .
It is done when a
state function or
Module:init/1
@@ -242,9 +227,7 @@ erlang:'!' -----> Module:StateName/3
However, use this feature with care,
as hibernation can be too costly
to use after every event; see
-
- erlang:hibernate/3
- .
+ erlang:hibernate/3 .
@@ -257,7 +240,7 @@ erlang:'!' -----> Module:StateName/3
state_functions .
You can push the button and it replies if it went on or off,
and you can ask for a count of how many times it has been
- pushed to on.
+ pushed to switch on.
The following is the complete callback module file
pushbutton.erl :
@@ -381,13 +364,10 @@ handle_event(_, _, State, Data) ->
Name specification to use when starting
a gen_statem server. See
-
- start_link/3
-
+ start_link/3
and
-
- server_ref()
- below.
+ server_ref()
+ below.
@@ -398,14 +378,12 @@ handle_event(_, _, State, Data) ->
Server specification to use when addressing
a gen_statem server.
See call/2 and
-
- server_name()
-
+ server_name()
above.
It can be:
- pid() | LocalName
+ pid() | LocalName
-
The gen_statem is locally registered.
@@ -421,21 +399,21 @@ handle_event(_, _, State, Data) ->
{global,GlobalName}
-
- The gen_statem is globally registered
- in kernel:global .
+ The gen_statem is globally registered in
+ kernel:global .
{via,RegMod,ViaName}
-
- The gen_statem is registered through
+ The gen_statem is registered in
an alternative process registry.
The registry callback module RegMod
is to export functions
register_name/2 , unregister_name/1 ,
whereis_name/1 , and send/2 ,
- which are to behave like the corresponding functions
- in kernel:global .
+ which are to behave like the corresponding functions in
+ kernel:global .
Thus, {via,global,GlobalName} is the same as
{global,GlobalName} .
@@ -482,9 +460,7 @@ handle_event(_, _, State, Data) ->
Destination to use when replying through, for example, the
-
- action()
-
+ action()
{reply,From,Reply}
to a process that has called the gen_statem server using
call/2 .
@@ -554,9 +530,7 @@ handle_event(_, _, State, Data) ->
or when calling
enter_loop/5,6,7 ,
and with the return value from
-
- Module:code_change/4
- .
+ Module:code_change/4 .
state_functions
@@ -565,9 +539,7 @@ handle_event(_, _, State, Data) ->
The state must be of type
state_name()
and one callback function per state, that is,
-
- Module:StateName/3
- ,
+ Module:StateName/3 ,
is used.
@@ -575,9 +547,7 @@ handle_event(_, _, State, Data) ->
-
The state can be any term and the callback function
-
- Module:handle_event/4
-
+ Module:handle_event/4
is used for all states.
@@ -604,9 +574,7 @@ handle_event(_, _, State, Data) ->
-
If
-
- postpone()
-
+ postpone()
is true ,
the current event is postponed.
@@ -620,9 +588,7 @@ handle_event(_, _, State, Data) ->
-
All events stored with
-
- action()
-
+ action()
next_event
are inserted in the queue to be processed before
all other events.
@@ -631,13 +597,9 @@ handle_event(_, _, State, Data) ->
-
If an
-
- event_timeout()
-
+ event_timeout()
is set through
-
- action()
-
+ action()
timeout ,
an event timer can be started or a time-out zero event
can be enqueued.
@@ -651,9 +613,7 @@ handle_event(_, _, State, Data) ->
otherwise the gen_statem goes into receive
or hibernation
(if
-
- hibernate()
-
+ hibernate()
is true )
to wait for the next message. In hibernation the next
non-system event awakens the gen_statem , or rather
@@ -681,16 +641,13 @@ handle_event(_, _, State, Data) ->
If true , hibernates the gen_statem
by calling
-
- proc_lib:hibernate/3
-
+ proc_lib:hibernate/3
before going into receive
to wait for a new external event.
If there are enqueued events,
to prevent receiving any new event, an
-
- erlang:garbage_collect/0
- is done instead to simulate
+ erlang:garbage_collect/0
+ is done instead to simulate
that the gen_statem entered hibernation
and immediately got awakened by the oldest enqueued event.
@@ -731,8 +688,8 @@ handle_event(_, _, State, Data) ->
These state transition actions can be invoked by
returning them from the
- state function ,
- from Module:init/1
+ state function , from
+ Module:init/1
or by giving them to
enter_loop/6,7 .
@@ -741,15 +698,11 @@ handle_event(_, _, State, Data) ->
Actions that set
-
- transition options
-
+ transition options
override any previous of the same type,
so the last in the containing list wins.
For example, the last
-
- event_timeout()
-
+ event_timeout()
overrides any other event_timeout() in the list.
@@ -757,12 +710,8 @@ handle_event(_, _, State, Data) ->
-
Sets the
-
- transition_option()
-
-
- postpone()
-
+ transition_option()
+ postpone()
for this state transition.
This action is ignored when returned from
Module:init/1
@@ -775,12 +724,8 @@ handle_event(_, _, State, Data) ->
-
Sets the
-
- transition_option()
-
-
- hibernate()
-
+ transition_option()
+ hibernate()
for this state transition.
@@ -792,22 +737,16 @@ handle_event(_, _, State, Data) ->
This form exists to make the
state function
return value {next_state,NextState,NewData,Timeout}
- allowed like for
-
- gen_fsm:Module:StateName/2
- .
+ allowed like for gen_fsm 's
+ Module:StateName/2 .
timeout
-
Sets the
-
- transition_option()
-
-
- event_timeout()
-
+ transition_option()
+ event_timeout()
to Time with EventContent .
@@ -832,9 +771,7 @@ handle_event(_, _, State, Data) ->
An event of type
-
- internal
-
+ internal
is to be used when you want to reliably distinguish
an event inserted this way from any external event.
@@ -849,9 +786,7 @@ handle_event(_, _, State, Data) ->
Replies to a caller waiting for a reply in
call/2 .
From must be the term from argument
-
- {call,From }
-
+ {call,From }
to the
state function .
@@ -907,9 +842,7 @@ handle_event(_, _, State, Data) ->
-
Terminates the gen_statem by calling
-
- Module:terminate/3
-
+ Module:terminate/3
with Reason and
NewData , if specified.
@@ -919,9 +852,7 @@ handle_event(_, _, State, Data) ->
Sends all Replies ,
then terminates the gen_statem by calling
-
- Module:terminate/3
-
+ Module:terminate/3
with Reason and
NewData , if specified.
@@ -965,9 +896,7 @@ handle_event(_, _, State, Data) ->
Makes a synchronous call to the gen_statem
-
- ServerRef
-
+ ServerRef
by sending a request
and waiting until its reply arrives.
The gen_statem calls the
@@ -1015,9 +944,7 @@ handle_event(_, _, State, Data) ->
Sends an asynchronous event to the gen_statem
-
- ServerRef
-
+ ServerRef
and returns ok immediately,
ignoring if the destination node or gen_statem
does not exist.
@@ -1038,9 +965,7 @@ handle_event(_, _, State, Data) ->
The same as
enter_loop/7
except that no
-
- server_name()
-
+ server_name()
must have been registered.
@@ -1055,9 +980,7 @@ handle_event(_, _, State, Data) ->
the same as
enter_loop/7
except that no
-
- server_name()
-
+ server_name()
must have been registered and
Actions = Server_or_Actions .
@@ -1095,13 +1018,9 @@ handle_event(_, _, State, Data) ->
Module , Opts , and
Server have the same meanings
as when calling
-
- start[_link]/3,4
- .
+ start[_link]/3,4 .
However, the
-
- server_name()
-
+ server_name()
name must have been registered accordingly
before this function is called.
@@ -1137,16 +1056,12 @@ handle_event(_, _, State, Data) ->
From must be the term from argument
-
- {call,From }
-
+ {call,From }
to the
state function .
From and Reply
can also be specified using a
-
- reply_action()
-
+ reply_action()
and multiple replies with a list of them.
@@ -1204,9 +1119,7 @@ handle_event(_, _, State, Data) ->
ServerName specifies the
-
- server_name()
-
+ server_name()
to register for the gen_statem .
If the gen_statem is started with start_link/3 ,
no ServerName is provided and
@@ -1225,8 +1138,7 @@ handle_event(_, _, State, Data) ->
Opts , the gen_statem
is allowed to spend Time milliseconds initializing
or it terminates and the start function returns
-
- {error,timeout} .
+ {error,timeout} .
-
@@ -1241,8 +1153,8 @@ handle_event(_, _, State, Data) ->
If option {spawn_opt,SpawnOpts} is present in
Opts , SpawnOpts is passed
- as option list to
- erlang:spawn_opt/2 ,
+ as option list to
+ erlang:spawn_opt/2 ,
which is used to spawn the gen_statem process.
@@ -1250,22 +1162,19 @@ handle_event(_, _, State, Data) ->
Using spawn option monitor is not
- allowed, it cause this function to fail with reason
+ allowed, it causes this function to fail with reason
badarg .
If the gen_statem is successfully created
and initialized, this function returns
-
- {ok,Pid}
- ,
+ {ok,Pid} ,
where Pid is the pid()
of the gen_statem .
If a process with the specified ServerName
exists already, this function returns
-
- {error,{already_started,Pid}} ,
+ {error,{already_started,Pid}} ,
where Pid is the pid() of that process.
@@ -1273,16 +1182,12 @@ handle_event(_, _, State, Data) ->
this function returns
{error,Reason} .
If Module:init/1 returns
-
- {stop,Reason}
-
+ {stop,Reason}
or
ignore ,
the process is terminated and this function
returns
-
- {error,Reason}
-
+ {error,Reason}
or
ignore ,
respectively.
@@ -1296,9 +1201,7 @@ handle_event(_, _, State, Data) ->
The same as
-
- stop(ServerRef , normal, infinity) .
-
+ stop(ServerRef , normal, infinity) .
@@ -1309,15 +1212,11 @@ handle_event(_, _, State, Data) ->
Orders the gen_statem
-
- ServerRef
-
+ ServerRef
to exit with the specified Reason
and waits for it to terminate.
The gen_statem calls
-
- Module:terminate/3
-
+ Module:terminate/3
before exiting.
@@ -1325,9 +1224,7 @@ handle_event(_, _, State, Data) ->
with the expected reason. Any other reason than normal ,
shutdown , or {shutdown,Term} causes an
error report to be issued through
-
- error_logger:format/2
- .
+ error_logger:format/2 .
The default Reason is normal .
@@ -1388,9 +1285,7 @@ handle_event(_, _, State, Data) ->
where Change={advanced,Extra} , is specified in the
appup
file. For more information, see
-
- OTP Design Principles
- .
+ OTP Design Principles .
For an upgrade, OldVsn is Vsn , and
@@ -1403,9 +1298,7 @@ handle_event(_, _, State, Data) ->
If you would dare to change
-
- callback mode
-
+ callback mode
during release upgrade/downgrade, the upgrade is no problem,
as the new code surely knows what callback mode
it needs. However, for a downgrade this function must
@@ -1483,8 +1376,7 @@ handle_event(_, _, State, Data) ->
return {CallbackMode,State,Data} or
{CallbackMode,State,Data,Actions} .
CallbackMode selects the
-
- callback mode
+ callback mode
of the gen_statem .
State is the initial
state()
@@ -1549,9 +1441,7 @@ handle_event(_, _, State, Data) ->
-
One of
-
- sys:get_status/1,2
-
+ sys:get_status/1,2
is invoked to get the gen_statem status. Opt is set
to the atom normal for this case.
@@ -1564,9 +1454,7 @@ handle_event(_, _, State, Data) ->
This function is useful for changing the form and
appearance of the gen_statem status for these cases. A
callback module wishing to change the
-
- sys:get_status/1,2
-
+ sys:get_status/1,2
return value and how
its status appears in termination error logs exports an
instance of format_status/2 , which returns a term
@@ -1589,9 +1477,7 @@ handle_event(_, _, State, Data) ->
changes the details of the current state and status of
the gen_statem . There are no restrictions on the
form Status can take, but for the
-
- sys:get_status/1,2
-
+ sys:get_status/1,2
case (when Opt
is normal ), the recommended form for
the Status value is [{data, [{"State",
@@ -1599,9 +1485,7 @@ handle_event(_, _, State, Data) ->
the gen_statem state. Following this recommendation is not
required, but it makes the callback module status
consistent with the rest of the
-
- sys:get_status/1,2
-
+ sys:get_status/1,2
return value.
@@ -1642,15 +1526,11 @@ handle_event(_, _, State, Data) ->
StateFunctionResult =
-
- state_function_result()
-
+ state_function_result()
HandleEventResult =
-
- handle_event_result()
-
+ handle_event_result()
@@ -1766,9 +1646,7 @@ handle_event(_, _, State, Data) ->
shutdown , or {shutdown,Term} ,
the gen_statem is assumed to terminate because of an error
and an error report is issued using
-
- error_logger:format/2 .
-
+ error_logger:format/2 .
This function can use
@@ -1781,11 +1659,13 @@ handle_event(_, _, State, Data) ->
See Also
- gen_event(3) ,
+
+ gen_event(3) ,
gen_fsm(3) ,
gen_server(3) ,
proc_lib(3) ,
supervisor(3) ,
- sys(3)
+ sys(3) .
+
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index 8b0fbed7c0..585b1a35f5 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -84,8 +84,9 @@ State(S) x Event(E) -> Actions(A), State(S')
a server Data besides the state. Because of this, and as
there is no restriction on the number of states
(assuming that there is enough virtual machine memory)
- or on the number of distinct input events, makes
- a state machine implemented with this behavior Turing complete.
+ or on the number of distinct input events,
+ a state machine implemented with this behavior
+ is in fact Turing complete.
But it feels mostly like an Event-Driven Mealy Machine.
- In mode
- In mode
handle_event(EventType, EventContent, State, Data) -> .. code for actions here ... - {next_state, State', Data'}+ {next_state, NewState, NewData}
Both these modes allow other return tuples; see
-
@@ -291,9 +290,7 @@ start_link(Code) -> ]]>
The fourth argument,
Function
-
Function
-
The function notifying the code lock about a button event is
implemented using
-
@@ -528,9 +509,7 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->
]]>
This example uses
-
If mode
strategy must be a time-out value and the gen_statem must
in function init/1 set itself to trap exit signals
by calling
-
- process_flag(trap_exit, true)
- .
- When ordered to shut down, the gen_statem then calls
- callback function terminate(shutdown, State, Data) :
+ process_flag(trap_exit, true) :
@@ -616,6 +589,10 @@ init(Args) ->
do_lock(),
...
]]>
+
+ When ordered to shut down, the gen_statem then calls
+ callback function terminate(shutdown, State, Data) .
+
In the following example, function terminate/3
locks the door if it is open, so we do not accidentally leave the door
@@ -633,9 +610,7 @@ terminate(_Reason, State, _Data) ->
If the gen_statem is not part of a supervision tree,
it can be stopped using
-
- gen_statem:stop
- ,
+ gen_statem:stop ,
preferably through an API function:
Actions
In the first sections actions were mentioned as a part of
- the general state machine model. These actions
+ the general state machine model. These general actions
are implemented with the code that callback module
gen_statem executes in an event-handling
callback function before returning
@@ -671,17 +646,11 @@ stop() ->
that a callback function can order the gen_statem
engine to do after the callback function return.
These are ordered by returning a list of
-
- actions
-
+ actions
in the
-
- return tuple
-
+ return tuple
from the
-
- callback function
- .
+ callback function .
These state transition actions affect the gen_statem
engine itself and can do the following:
@@ -697,9 +666,7 @@ stop() ->
and replying to a caller.
An example of event postponing is included later in this chapter.
For details, see the
-
- gen_statem(3)
-
+ gen_statem(3)
manual page.
You can, for example, reply to many callers
and generate multiple next events to handle.
@@ -712,9 +679,7 @@ stop() ->
Event Types
The previous sections mentioned a few
-
- event types
- .
+ event types .
Events of all types are handled in the same callback function,
for a given state, and the function gets
EventType and EventContent as arguments.
@@ -727,22 +692,16 @@ stop() ->
cast
-
Generated by
-
- gen_statem:cast .
-
+ gen_statem:cast .
{call,From}
-
Generated by
-
- gen_statem:call
- ,
+ gen_statem:call ,
where From is the reply address to use
when replying either through the state transition action
{reply,From,Msg} or by calling
-
- gen_statem:reply
- .
+ gen_statem:reply .
info
-
@@ -759,7 +718,7 @@ stop() ->
-
Generated by state transition action
{next_event,internal,EventContent} .
- All event types above can be generated using
+ All event types above can also be generated using
{next_event,EventType,EventContent} .
@@ -780,9 +739,7 @@ stop() ->
or you want to start a timer in one state and respond
to the time-out in another. This can be accomplished
with a regular Erlang timer:
-
- erlang:start_timer .
-
+ erlang:start_timer .
For the example so far in this chapter: using the
@@ -818,9 +775,7 @@ open(cast, {button,_}, Data) ->
]]>
If you need to cancel a timer because of some other event, you can use
-
- erlang:cancel_timer(Tref)
- .
+ erlang:cancel_timer(Tref) .
Notice that a time-out message cannot arrive after this,
unless you have postponed it (see the next section) before,
so ensure that you do not accidentally postpone such messages.
@@ -844,9 +799,7 @@ open(cast, {button,_}, Data) ->
Postponing is ordered by the state transition
-
- action
-
+ action
postpone .
@@ -861,7 +814,7 @@ open(cast, {button,_}, Data) ->
...
]]>
- A postponed event is only retried after a state change
+ The fact that a postponed event is only retried after a state change
translates into a requirement on the event and state space.
If you have a choice between storing a state data item
in the
The state transition
-
You can generate events of any existing
-
This section describes what to change in the example
to use one
To avoid this, you can format the internal state
that gets in the error log and gets returned from
-
- sys:get_status/1,2
-
+ sys:get_status/1,2
by implementing function
-
- Module:format_status/2
- ,
+ Module:format_status/2 ,
for example like this:
]]>
It is not mandatory to implement a
-
- Module:format_status/2
-
+ Module:format_status/2
function. If you do not, a default implementation is used that
does the same as this example function without filtering
the Data term, that is, StateData = {State,Data} .
@@ -1274,13 +1215,9 @@ format_status(Opt, [_PDict,State,Data]) ->
Complex State
The callback mode
-
- handle_event_function
-
+ handle_event_function
enables using a non-atom state as described in section
-
- Callback Modes
- ,
+ Callback Modes ,
for example, a complex state term like a tuple.
@@ -1308,8 +1245,7 @@ format_status(Opt, [_PDict,State,Data]) ->
So we make the button/1 function synchronous
by using
-
- gen_statem:call
+ gen_statem:call
and still postpone its events in the open state.
Then a call to button/1 during the open
state does not return until the state transits to locked ,
@@ -1462,16 +1398,12 @@ format_status(Opt, [_PDict,State,Data]) ->
and the amount of heap memory all these servers need
is a problem, then the memory footprint of a server
can be mimimized by hibernating it through
-
- proc_lib:hibernate/3 .
-
+ proc_lib:hibernate/3 .
It is rather costly to hibernate a process; see
-
- erlang:hibernate/3
- .
+ erlang:hibernate/3 .
It is not something you want to do after every event.
@@ -1495,9 +1427,7 @@ handle_event(
]]>
The
-