diff options
Diffstat (limited to 'system/doc/design_principles/statem.xml')
-rw-r--r-- | system/doc/design_principles/statem.xml | 123 |
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> |