From d86fd35ca0c65069955a34d6ae9fbc33b9663eb0 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen
Date: Tue, 20 Mar 2018 03:34:23 -0700
Subject: Update User's Guide and pointers to it
---
system/doc/design_principles/statem.xml | 543 ++++++++++++++++++++++++--------
1 file changed, 412 insertions(+), 131 deletions(-)
(limited to 'system/doc')
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index 5be2981f62..16f6ce8348 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -36,16 +36,6 @@
manual page in STDLIB, where all interface functions and callback
functions are described in detail.
-
-
- 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.
-
-
@@ -92,6 +82,31 @@ State(S) x Event(E) -> Actions(A), State(S')
+
+
+
+
+ Callback Module
+
+ The callback module contains functions that implement
+ the state machine.
+ When an event occurs,
+ the gen_statem behaviour engine
+ calls a function in the callback module with the event,
+ current state and server data.
+ This function performs the actions for this event,
+ and returns the new state and server data
+ and also actions to be performed by the behaviour engine.
+
+
+ The behaviour engine holds the state machine state,
+ server data, timer references, a queue of posponed messages
+ and other metadata. It receives all process messages,
+ handles the system messages, and calls the callback module
+ with machine specific events.
+
+
+
@@ -100,54 +115,65 @@ State(S) x Event(E) -> Actions(A), State(S')
The gen_statem behavior supports two callback modes:
-
+
+
+
+ state_functions
+
+
-
- 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}.
-
-
- This form is used in most examples here for example in section
- Example.
+ Events are handled by one callback functions per state.
+
+
+ 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, NewState, NewData}
-
-
- See section
- One Event Handler
- for an example.
+ Events are handled by one single callback function.
-
+
- Both these modes allow other return tuples; see
- 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.
+ The callback mode is selected at server start
+ and may be changed with a code upgrade/downgrade.
+
+
+ See the section
+ Event Handler
+ that describes the event handling callback function(s).
+
+
+ The callback mode is selected by implementing a callback function
+
+ Module:callback_mode()
+
+ that returns one of the callback modes.
+
+
+ The
+
+ Module:callback_mode()
+
+ function may also return a list containing the callback mode
+ and the atom state_enter in which case
+ state enter calls
+ are activated for the callback mode.
Choosing the Callback Mode
+
+ The short version: choose state_functions -
+ it is the one most like gen_fsm.
+ But if you do not want the restriction that the state
+ must be an atom, or if having to write an event handler function
+ per state is not as you like it; please read on...
+
The two
callback modes
@@ -186,7 +212,9 @@ 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 branching to
helper functions.
@@ -208,36 +236,166 @@ handle_event(EventType, EventContent, State, Data) ->
-
- State Enter Calls
+
+ Event Handler
- The gen_statem behavior can regardless of callback mode
- automatically
-
- call the state callback
-
- with special arguments whenever the state changes
- so you can write state entry actions
- near the rest of the state transition rules.
- It typically looks like this:
+ Which callback function that handles an event
+ depends on the callback mode:
-
-StateName(enter, _OldState, Data) ->
- ... code for state entry actions here ...
- {keep_state, NewData};
-StateName(EventType, EventContent, Data) ->
- ... code for actions here ...
- {next_state, NewStateName, NewData}.
+
+ state_functions
+ -
+ The event is handled by:
+
+ Module:StateName(EventType, EventContent, Data)
+
+
+ This form is the one mostly used in the
+ Example
+ section.
+
+
+ handle_event_function
+ -
+ The event is handled by:
+
+ Module:handle_event(EventType, EventContent, State, Data)
+
+
+ See section
+ One Event Handler
+ for an example.
+
+
+
- Depending on how your state machine is specified,
- this can be a very useful feature,
- but it forces you to handle the state enter calls in all states.
- See also the
-
- State Entry Actions
+ The state is either the name of the function itself or an argument to it.
+ The other arguments are the EventType described in section
+ Event Types,
+ the event dependent EventContent, and the current server Data.
+
+
+ State enter calls are also handled by the event handler and have
+ slightly different arguments. See the section
+ State Enter Calls.
+
+
+ The event handler return values are defined in the description of
+
+ Module:StateName/3
- chapter.
+ in the gen_statem manual page, but here is
+ a more readable list:
+
+
+ {next_state, NextState, NewData, Actions}
+ {next_state, NextState, NewData}
+
+ -
+
+ Set next state and update the server data.
+ If the Actions field is used, execute state transition actions.
+ An empty Actions list is equivalent to not returning the field.
+
+
+ See section
+ Actions for a list of possible
+ state transition actions.
+
+
+ If NextState =/= State the state machine changes
+ to a new state. A
+ state enter call
+ is performed if enabled and all
+ postponed events
+ are retried.
+
+
+
+ {keep_state, NewData, Actions}
+ {keep_state, NewData}
+
+ -
+
+ Same as the next_state values with
+ NextState =:= State, that is no state change.
+
+
+
+ {keep_state_and_data, Actions}
+ keep_state_and_data
+
+ -
+
+ Same as the keep_state values with
+ NextData =:= Data, that is no change in server data.
+
+
+
+ {repeat_state, NewData, Actions}
+ {repeat_state, NewData}
+ {repeat_state_and_data, Actions}
+ repeat_state_and_data
+
+ -
+
+ Same as the keep_state or keep_state_and_data values,
+ and if state enter calls
+ are enabled, repeat that call.
+
+
+
+ {stop, Reason, NewData}
+ {stop, Reason}
+
+ -
+
+ Stop the server with reason Reason.
+ If the NewData field is used, first update the server data.
+
+
+
+ {stop_and_reply, Reason, NewData, Actions}
+ {stop_and_reply, Reason, Actions}
+
+ -
+
+ Same as the stop values, but first execute the given
+ state transition actions that may only be reply actions.
+
+
+
+
+
+
+ The First State
+
+ To decide the first state the
+
+ Module:init(Args)
+
+ callback function is called before any
+ event handler
+ is called. This function behaves exactly as an event handler
+ function, but gets its only argument Args from
+ the gen_statem
+
+ start/3,4
+
+ or
+
+ start_link/3,4
+
+ function, and returns {ok, State, Data}
+ or {ok, State, Data, Actions}.
+ If you use the
+ postpone
+ action from this function, that action is ignored,
+ since there is no event to postpone.
+
+
+
@@ -246,10 +404,8 @@ StateName(EventType, EventContent, Data) ->
Actions
- In the first section
-
- Event-Driven State Machines
-
+ In the first
+ section
actions were mentioned as a part of
the general state machine model. These general actions
are implemented with the code that callback module
@@ -264,72 +420,97 @@ StateName(EventType, EventContent, Data) ->
These are ordered by returning a list of
actions
in the
- return tuple
+
+ return value
+
from the
callback function.
- These state transition actions affect the gen_statem
- engine itself and can do the following:
+ These are the possible state transition actions:
-
- -
+
+
- Postpone
+ postpone
- the current event, see section
+
+ {postpone, Boolean}
+
+ -
+ If set postpone the current event, see section
Postponing Events
- -
+
- Hibernate
+ hibernate
- the gen_statem, treated in
+
+ {hibernate, Boolean}
+
+ -
+ If set hibernate the gen_statem, treated in section
Hibernation
- -
- Start a
+
- state time-out,
- read more in section
+ {state_timeout, Time}
+
+
+ {state_timeout, Time, Opts}
+
+ -
+ Start a state time-out, read more in section
State Time-Outs
- -
- Start a
+
- generic time-out,
- read more in section
+ {{timeout, Name}, Time}
+
+
+ {{timeout, Name}, Time, Opts}
+
+ -
+ Start a generic time-out, read more in section
Generic Time-Outs
+
+
+ {timeout, Time}
+
+
+ {timeout, Time, Opts}
+ Time
+
-
- Start an
- event time-out,
- see more in section
+ Start an event time-out, see more in section
Event Time-Outs
- -
+
- Reply
+ {reply, From, Reply}
- to a caller, mentioned at the end of section
+
+
-
+ Reply to a caller, mentioned at the end of section
All State Events
- -
- Generate the
+
- next event
+ {next_event, EventType, EventContent}
- to handle, see section
- Self-Generated Events
+
+
-
+ Generate the next event to handle, see section
+ Inserted Events
-
+
- For details, see the
-
- gen_statem(3)
-
- manual page.
+ For details, see the gen_statem(3)
+ manual page for type
+ action().
You can, for example, reply to many callers,
generate multiple next events,
- and set time-outs to relative or absolute times.
+ and set a time-out to use absolute instead of relative time
+ (using the Opts field).
@@ -341,8 +522,8 @@ StateName(EventType, EventContent, Data) ->
Events are categorized in different
event types.
- Events of all types are handled in the same callback function,
- for a given state, and the function gets
+ Events of all types are for a given state
+ handled in the same callback function, and that function gets
EventType and EventContent as arguments.
@@ -350,12 +531,20 @@ StateName(EventType, EventContent, Data) ->
they come from:
- cast
+
+
+ cast
+
+
-
Generated by
gen_statem:cast.
- {call,From}
+
+
+ {call,From}
+
+
-
Generated by
gen_statem:call,
@@ -364,12 +553,20 @@ StateName(EventType, EventContent, Data) ->
{reply,From,Msg} or by calling
gen_statem:reply.
- info
+
+
+ info
+
+
-
Generated by any regular process message sent to
the gen_statem process.
- state_timeout
+
+
+ state_timeout
+
+
-
Generated by state transition action
@@ -377,7 +574,11 @@ StateName(EventType, EventContent, Data) ->
state timer timing out.
- {timeout,Name}
+
+
+ {timeout,Name}
+
+
-
Generated by state transition action
@@ -385,7 +586,11 @@ StateName(EventType, EventContent, Data) ->
generic timer timing out.
- timeout
+
+
+ timeout
+
+
-
Generated by state transition action
@@ -394,7 +599,11 @@ StateName(EventType, EventContent, Data) ->
(or its short form Time)
event timer timing out.
- internal
+
+
+ internal
+
+
-
Generated by state transition
action
@@ -405,6 +614,61 @@ StateName(EventType, EventContent, Data) ->
+
+
+
+
+ State Enter Calls
+
+ The gen_statem behavior can if this is enabled,
+ regardless of callback mode,
+ automatically
+
+ call the state callback
+
+ with special arguments whenever the state changes
+ so you can write state enter actions
+ near the rest of the state transition rules.
+ It typically looks like this:
+
+
+StateName(enter, OldState, Data) ->
+ ... code for state enter actions here ...
+ {keep_state, NewData};
+StateName(EventType, EventContent, Data) ->
+ ... code for actions here ...
+ {next_state, NewStateName, NewData}.
+
+ Since the state enter call is not an event there are restrictions
+ on the allowed return value and
+ state transition actions.
+ You may not change the state,
+ postpone
+ this non-event, or
+ insert events.
+
+
+ The first state that is entered will get a state enter call
+ with OldState equal to the current state.
+
+
+ You may repeat the state enter call using the {repeat_state,...}
+ return value from the
+ event handler.
+ In this case OldState will also be equal to the current state.
+
+
+ Depending on how your state machine is specified,
+ this can be a very useful feature,
+ but it forces you to handle the state enter calls in all states.
+ See also the
+
+ State Enter Actions
+
+ chapter.
+
+
+
@@ -1196,14 +1460,14 @@ do_unlock() ->
-
- State Entry Actions
+
+ State Enter Actions
Say you have a state machine specification
- that uses state entry actions.
- Allthough you can code this using self-generated events
+ that uses state enter actions.
+ Allthough you can code this using inserted events
(described in the next section), especially if just
- one or a few states has got state entry actions,
+ one or a few states has got state enter actions,
this is a perfect use case for the built in
state enter calls.
@@ -1244,7 +1508,7 @@ open(state_timeout, lock, Data) ->
...
]]>
- You can repeat the state entry code by returning one of
+ You can repeat the state enter code by returning one of
{repeat_state, ...}, {repeat_state_and_data,_}
or repeat_state_and_data that otherwise behaves
exactly like their keep_state siblings.
@@ -1259,8 +1523,8 @@ open(state_timeout, lock, Data) ->
-
- Self-Generated Events
+
+ Inserted Events
It can sometimes be beneficial to be able to generate events
to your own state machine.
@@ -1279,14 +1543,18 @@ open(state_timeout, lock, Data) ->
One example for this is to pre-process incoming data, for example
decrypting chunks or collecting characters up to a line break.
+
+
Purists may argue that this should be modelled with a separate
state machine that sends pre-processed events
- to the main state machine.
- But to decrease overhead the small pre-processing state machine
+ to the main state machine,
+ but to decrease overhead the small pre-processing state machine
can be implemented in the common state event handling
of the main state machine using a few state data variables
that then sends the pre-processed events as internal events
to the main state machine.
+ Using internal events also can make it easier
+ to synchronize the state machines.
The following example uses an input model where you give the lock
@@ -1800,10 +2068,23 @@ handle_event(
Another not uncommon scenario is to use the event time-out
- to triger hibernation after a certain time of inactivity.
+ to trigger hibernation after a certain time of inactivity.
+ There is also a server start option
+
+ {hibernate_after, Timeout}
+
+ for
+
+ start/3,4
+
+ or
+
+ start_link/3,4
+
+ that may be used to automatically hibernate the server.
- This server probably does not use
+ This particular server probably does not use
heap memory worth hibernating for.
To gain anything from hibernation, your server would
have to produce some garbage during callback execution,
--
cgit v1.2.3