From dfa3b2414384f1ee0cdc062a23df5f1094a7069e Mon Sep 17 00:00:00 2001
From: Henrik Nord Certificates can be decoded as usual:
- 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
1> binary:matches(<<"abcde">>,
- [<<"bcde">>,<<"bc">>>,<<"de">>],[]).
+ [<<"bcde">>,<<"bc">>,<<"de">>],[]).
[{1,4}]
--
cgit v1.2.3
From 09d138e229846b7056331151135b7c8a52dc476f Mon Sep 17 00:00:00 2001
From: xsipewe
+
+
-
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