aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc/design_principles/statem.xml
diff options
context:
space:
mode:
Diffstat (limited to 'system/doc/design_principles/statem.xml')
-rw-r--r--system/doc/design_principles/statem.xml123
1 files changed, 98 insertions, 25 deletions
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index 22b622ec5f..7febe31df3 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -293,6 +293,13 @@ StateName(EventType, EventContent, Data) ->
<seealso marker="#State Time-Outs">State Time-Outs</seealso>
</item>
<item>
+ Start a
+ <seealso marker="stdlib:gen_statem#type-generic_timeout">
+ generic time-out</seealso>,
+ read more in section
+ <seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>
+ </item>
+ <item>
Start an
<seealso marker="stdlib:gen_statem#type-event_timeout">event time-out</seealso>,
see more in section
@@ -320,8 +327,9 @@ StateName(EventType, EventContent, Data) ->
<c>gen_statem(3)</c>
</seealso>
manual page.
- You can, for example, reply to many callers
- and generate multiple next events to handle.
+ You can, for example, reply to many callers,
+ generate multiple next events,
+ and set time-outs to relative or absolute times.
</p>
</section>
@@ -369,6 +377,14 @@ StateName(EventType, EventContent, Data) ->
</seealso>
state timer timing out.
</item>
+ <tag><c>{timeout,Name}</c></tag>
+ <item>
+ Generated by state transition action
+ <seealso marker="stdlib:gen_statem#type-generic_timeout">
+ <c>{{timeout,Name},Time,EventContent}</c>
+ </seealso>
+ generic timer timing out.
+ </item>
<tag><c>timeout</c></tag>
<item>
Generated by state transition action
@@ -450,7 +466,7 @@ locked(
[Digit] ->
do_unlock(),
{next_state, open, Data#{remaining := Code},
- [{state_timeout,10000,lock}];
+ [{state_timeout,10000,lock}]};
[Digit|Rest] -> % Incomplete
{next_state, locked, Data#{remaining := Rest}};
_Wrong ->
@@ -779,7 +795,7 @@ handle_event(cast, {button,Digit}, State, #{code := Code} = Data) ->
[Digit] -> % Complete
do_unlock(),
{next_state, open, Data#{remaining := Code},
- [{state_timeout,10000,lock}};
+ [{state_timeout,10000,lock}]};
[Digit|Rest] -> % Incomplete
{keep_state, Data#{remaining := Rest}};
[_|_] -> % Wrong
@@ -873,7 +889,7 @@ stop() ->
<marker id="Event Time-Outs" />
<title>Event Time-Outs</title>
<p>
- A timeout feature inherited from <c>gen_statem</c>'s predecessor
+ A time-out feature inherited from <c>gen_statem</c>'s predecessor
<seealso marker="stdlib:gen_fsm"><c>gen_fsm</c></seealso>,
is an event time-out, that is,
if an event arrives the timer is cancelled.
@@ -906,24 +922,24 @@ locked(
...
]]></code>
<p>
- Whenever we receive a button event we start an event timeout
+ Whenever we receive a button event we start an event time-out
of 30 seconds, and if we get an event type <c>timeout</c>
we reset the remaining code sequence.
</p>
<p>
- An event timeout is cancelled by any other event so you either
- get some other event or the timeout event. It is therefore
- not possible nor needed to cancel or restart an event timeout.
+ An event time-out is cancelled by any other event so you either
+ get some other event or the time-out event. It is therefore
+ not possible nor needed to cancel or restart an event time-out.
Whatever event you act on has already cancelled
- the event timeout...
+ the event time-out...
</p>
</section>
<!-- =================================================================== -->
<section>
- <marker id="Erlang Timers" />
- <title>Erlang Timers</title>
+ <marker id="Generic Time-Outs" />
+ <title>Generic Time-Outs</title>
<p>
The previous example of state time-outs only work if
the state machine stays in the same state during the
@@ -934,13 +950,68 @@ locked(
You may want to start a timer in one state and respond
to the time-out in another, maybe cancel the time-out
without changing states, or perhaps run multiple
- time-outs in parallel. All this can be accomplished
- with Erlang Timers:
+ time-outs in parallel. All this can be accomplished with
+ <seealso marker="stdlib:gen_statem#type-generic_timeout">generic time-outs</seealso>.
+ They may look a little bit like
+ <seealso marker="stdlib:gen_statem#type-event_timeout">event time-outs</seealso>
+ but contain a name to allow for any number of them simultaneously
+ and they are not automatically cancelled.
+ </p>
+ <p>
+ Here is how to accomplish the state time-out
+ in the previous example by instead using a generic time-out
+ named <c>open_tm</c>:
+ </p>
+ <code type="erl"><![CDATA[
+...
+locked(
+ cast, {button,Digit},
+ #{code := Code, remaining := Remaining} = Data) ->
+ case Remaining of
+ [Digit] ->
+ do_unlock(),
+ {next_state, open, Data#{remaining := Code},
+ [{{timeout,open_tm},10000,lock}]};
+...
+
+open({timeout,open_tm}, lock, Data) ->
+ do_lock(),
+ {next_state,locked,Data};
+open(cast, {button,_}, Data) ->
+ {keep_state,Data};
+...
+ ]]></code>
+ <p>
+ Just as
+ <seealso marker="#State Time-Outs">state time-outs</seealso>
+ you can restart or cancel a specific generic time-out
+ by setting it to a new time or <c>infinity</c>.
+ </p>
+ <p>
+ Another way to handle a late time-out can be to not cancel it,
+ but to ignore it if it arrives in a state
+ where it is known to be late.
+ </p>
+ </section>
+
+<!-- =================================================================== -->
+
+ <section>
+ <marker id="Erlang Timers" />
+ <title>Erlang Timers</title>
+ <p>
+ The most versatile way to handle time-outs is to use
+ Erlang Timers; see
<seealso marker="erts:erlang#start_timer/4"><c>erlang:start_timer3,4</c></seealso>.
+ Most time-out tasks can be performed with the
+ time-out features in <c>gen_statem</c>,
+ but an example of one that can not is if you should need
+ the return value from
+ <seealso marker="erts:erlang#cancel_timer/2"><c>erlang:cancel_timer(Tref)</c></seealso>, that is; the remaining time of the timer.
</p>
<p>
Here is how to accomplish the state time-out
- in the previous example by insted using an Erlang Timer:
+ in the previous example by instead using an Erlang Timer:
</p>
<code type="erl"><![CDATA[
...
@@ -1511,10 +1582,12 @@ format_status(Opt, [_PDict,State,Data]) ->
for example, a complex state term like a tuple.
</p>
<p>
- One reason to use this is when you have
- a state item that affects the event handling,
- in particular in combination with postponing events.
- We complicate the previous example
+ One reason to use this is when you have a state item
+ that when changed should cancel the
+ <seealso marker="#State Time-Outs">state time-out</seealso>,
+ or one that affects the event handling
+ in combination with postponing events.
+ We will complicate the previous example
by introducing a configurable lock button
(this is the state item in question),
which in the <c>open</c> state immediately locks the door,
@@ -1596,7 +1669,7 @@ handle_event(
{call,From}, code_length,
{_StateName,_LockButton}, #{code := Code}) ->
{keep_state_and_data,
- [{reply,From,length(Code)}]};
+ [{reply,From,length(Code)}]};
%%
%% State: locked
handle_event(
@@ -1636,7 +1709,7 @@ handle_event(
if
Digit =:= LockButton ->
{next_state, {locked,LockButton}, Data,
- [{reply,From,locked}]);
+ [{reply,From,locked}]};
true ->
{keep_state_and_data,
[postpone]}
@@ -1710,10 +1783,10 @@ handle_event(
EventType, EventContent,
{open,LockButton}, Data) ->
case {EventType, EventContent} of
- {enter, _OldState} ->
- do_unlock(),
- {keep_state_and_data,
- [{state_timeout,10000,lock},hibernate]};
+ {enter, _OldState} ->
+ do_unlock(),
+ {keep_state_and_data,
+ [{state_timeout,10000,lock},hibernate]};
...
]]></code>
<p>