From 09d138e229846b7056331151135b7c8a52dc476f Mon Sep 17 00:00:00 2001
From: xsipewe
This section is to be read with the
- 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.
+
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:
These relations are interpreted as meaning:
- If we are in state These relations are interpreted as follows:
+ if we are in state
- Note that
- Since
- Like most
- The
+ In mode
- In the mode
-
+ In mode
- Both these modes allow other return tuples
- that you can find in the
+ Both these modes allow other return tuples; see
The two
- 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
- 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
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
- 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
- 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
-
- The first argument,
State(S) x Event(E) -> Actions(A), State(S')
-
+
+
StateName(EventType, EventContent, Data) ->
.. code for actions here ...
{next_state, NewStateName, NewData}.
-
+
handle_event(EventType, EventContent, State, Data) ->
.. code for actions here ...
{next_state, State', Data'}
+
gen_statem:start_link({local,?NAME}, ?MODULE, Code, []).
]]>
- 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
---
system/doc/design_principles/statem.xml | 184 ++++++++++----------------------
1 file changed, 57 insertions(+), 127 deletions(-)
(limited to 'system/doc/design_principles/statem.xml')
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.
@@ -101,8 +102,8 @@ State(S) x Event(E) -> Actions(A), State(S')
-
- In mode
- state_functions ,
+ In mode
+ state_functions ,
the state transition rules are written as some Erlang
functions, which conform to the following convention:
@@ -113,20 +114,19 @@ StateName(EventType, EventContent, Data) ->
-
- In mode
- handle_event_function ,
+ 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'}
+ {next_state, NewState, NewData}
Both these modes allow other return tuples; see
-
- Module:StateName/3
+ 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,
@@ -172,8 +172,7 @@ handle_event(EventType, EventContent, State, Data) ->
This mode works equally well when you want to focus on
one event at the time or on
one state at the time, but function
-
- Module:handle_event/4
+ Module:handle_event/4
quickly grows too large to handle without introducing dispatching.
@@ -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
-