From 30cae2492d8d8e927d57c0dc656ee2dfbec0a70c Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 13 Feb 2017 08:20:16 +0100 Subject: Implement {timeout,Name} timeouts --- system/doc/design_principles/statem.xml | 115 ++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 22 deletions(-) (limited to 'system/doc') diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml index f4d84ab163..f01615fdcd 100644 --- a/system/doc/design_principles/statem.xml +++ b/system/doc/design_principles/statem.xml @@ -4,7 +4,7 @@
- 2016 + 2016-2017 Ericsson AB. All Rights Reserved. @@ -292,6 +292,13 @@ StateName(EventType, EventContent, Data) -> read more in section State Time-Outs + + Start a + + generic time-out, + read more in section + Generic Time-Outs + Start an event time-out, @@ -320,8 +327,9 @@ StateName(EventType, EventContent, Data) -> gen_statem(3) 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.

@@ -369,6 +377,14 @@ StateName(EventType, EventContent, Data) -> state timer timing out.
+ {timeout,Name} + + Generated by state transition action + + {{timeout,Name},Time,EventContent} + + generic timer timing out. + timeout 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() -> Event Time-Outs

- A timeout feature inherited from gen_statem's predecessor + A time-out feature inherited from gen_statem's predecessor gen_fsm, is an event time-out, that is, if an event arrives the timer is cancelled. @@ -906,24 +922,24 @@ locked( ... ]]>

- 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 timeout we reset the remaining code sequence.

- 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...

- - Erlang Timers + + Generic Time-Outs

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 + generic time-outs. + They may look a little bit like + event time-outs + but contain a name to allow for any number of them simultaneously + and they are not automatically cancelled. +

+

+ Here is how to accomplish the state time-out + in the previous example by instead using a generic time-out + named open_tm: +

+ + 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}; +... + ]]> +

+ Just as + state time-outs + you can restart or cancel a specific generic time-out + by setting it to a new time or infinity. +

+

+ 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. +

+
+ + + +
+ + Erlang Timers +

+ The most versatile way to handle time-outs is to use + Erlang Timers; see erlang:start_timer3,4. + Most time-out tasks can be performed with the + time-out features in gen_statem, + but an example of one that can not is if you should need + the return value from + erlang:cancel_timer(Tref), that is; the remaining time of the timer.

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:

{keep_state_and_data, - [{reply,From,length(Code)}]}; + [{reply,From,length(Code)}]}; %% %% State: locked handle_event( @@ -1636,7 +1707,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 +1781,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]}; ... ]]>

-- cgit v1.2.3