From cea77ca09be156669c657592bebf6efc9d5cfaee Mon Sep 17 00:00:00 2001
From: Raimo Niskanen
Date: Thu, 25 Feb 2016 16:02:59 +0100
Subject: Write a simple example in the reference manual
---
lib/stdlib/doc/src/gen_statem.xml | 188 +++++++++++++++++++++++++++++---------
1 file changed, 144 insertions(+), 44 deletions(-)
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index 4af6732e7c..d50b88c561 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -91,7 +91,7 @@ erlang:'!' -----> Module:StateName/3
in a gen_statem is the callback function that is called
for all events in this state, and is selected depending on
callback_mode
- that the implementation specifies during gen_statem init.
+ that the implementation specifies when the the server starts.
When
@@ -103,7 +103,8 @@ erlang:'!' -----> Module:StateName/3
.
This gathers all code for a specific state
in one function and hence dispatches on state first.
- Note that in this mode the callback function
+ Note that in this mode the fact that there is
+ a mandatory callback function
Module:terminate/3
makes the state name terminate unusable.
@@ -115,9 +116,8 @@ erlang:'!' -----> Module:StateName/3
Module:handle_event/4
.
- This makes it easy to dispatch on state or on event as
- you like but you will have to implement it.
- Also be careful about which events you handle in which
+ This makes it easy to dispatch on state or on event as you desire.
+ Be careful about which events you handle in which
states so you do not accidentally postpone one event
forever creating an infinite busy loop.
@@ -132,9 +132,9 @@ erlang:'!' -----> Module:StateName/3
The gen_statem event queue model is sufficient
- to emulate the normal process message queue and selective receive
- with postponing an event corresponding to not matching
- it in a receive statement and changing states corresponding
+ to emulate the normal process message queue with selective receive.
+ Postponing an event corresponds to not matching it
+ in a receive statement and changing states corresponds
to entering a new receive statement.
@@ -149,8 +149,8 @@ erlang:'!' -----> Module:StateName/3
event_type() internal
- that can be used for such events making it impossible
- to mistake for an external event.
+ that can be used for such events making them impossible
+ to mistake for external events.
Inserting an event replaces the trick of calling your own
@@ -165,7 +165,7 @@ erlang:'!' -----> Module:StateName/3
See the type
- transition_option().
+ transition_option()
for the details of a state transition.
@@ -192,7 +192,7 @@ erlang:'!' -----> Module:StateName/3
) if a
state function or
Module:init/1
- specifies 'hibernate' in the returned
+ specifies hibernate in the returned
Actions
list. This might be useful if the server is expected to be idle
for a long time. However use this feature with care
@@ -202,6 +202,99 @@ erlang:'!' -----> Module:StateName/3
+
+ EXAMPLE
+
+ This example shows a simple pushbutton model
+ for a toggling pushbutton implemented with
+
+ callback_mode()
+
+ state_functions.
+ You can push the button and it replies if it went on or off,
+ and you can ask for a count of how many times it has been
+ pushed to on.
+
+ This is the complete callback module file pushbutton.erl:
+
+-module(pushbutton).
+-behaviour(gen_statem).
+
+-export([start/0,push/0,get_count/0,stop/0]).
+-export([terminate/3,code_change/4,init/1]).
+-export([on/3,off/3]).
+
+name() -> pushbutton_statem. % The registered server name
+
+%% API. This example uses a registered name name()
+%% and does not link to the caller.
+start() ->
+ gen_statem:start({local,name()}, ?MODULE, [], []).
+push() ->
+ gen_statem:call(name(), push).
+get_count() ->
+ gen_statem:call(name(), get_count).
+stop() ->
+ gen_statem:stop(name()).
+
+%% Mandatory callback functions
+terminate(_Reason, _State, _Data) ->
+ void.
+code_change(_Vsn, State, Data, _Extra) ->
+ {ok,{State,Data}}.
+init([]) ->
+ %% Set the callback mode and initial state + data.
+ %% Data is used only as a counter.
+ State = off, Data = 0,
+ {state_functions,State,Data}.
+
+
+%%% State functions
+
+off({call,Caller}, push, Data) ->
+ %% Go to 'on', increment count and reply
+ %% that the resulting status is 'on'
+ {next_state,on,Data+1,[{reply,Caller,on}]};
+off(EventType, EventContent, Data) ->
+ handle_event(EventType, EventContent, Data).
+
+on({call,Caller}, push, Data) ->
+ %% Go to 'off' and reply that the resulting status is 'off'
+ {next_state,off,Data,[{reply,Caller,off}]};
+on(EventType, EventContent, Data) ->
+ handle_event(EventType, EventContent, Data).
+
+%% Handle events common to all states
+handle_event({call,Caller}, get_count, Data) ->
+ %% Reply with the current count
+ {keep_state,Data,[{reply,Caller,Data}]};
+handle_event(_, _, Data) ->
+ %% Ignore all other events
+ {keep_state,Data}.
+
+ And this is a shell session when running it:
+
+1> pushbutton:start().
+{ok,<0.36.0>}
+2> pushbutton:get_count().
+0
+3> pushbutton:push().
+on
+4> pushbutton:get_count().
+1
+5> pushbutton:push().
+off
+6> pushbutton:get_count().
+1
+7> pushbutton:stop().
+ok
+8> pushbutton:push().
+** exception exit: {noproc,{gen_statem,call,[pushbutton_statem,push,infinity]}}
+ in function gen:do_for_proc/2 (gen.erl, line 261)
+ in call from gen_statem:call/3 (gen_statem.erl, line 386)
+
+
+
@@ -327,7 +420,7 @@ erlang:'!' -----> Module:StateName/3
callback_mode
- is state_functions, which is the default,
+ is state_functions,
the state has to be of this type.
@@ -353,11 +446,14 @@ erlang:'!' -----> Module:StateName/3
External events are of 3 different type:
{call,Caller}, cast or info.
- Calls (synchronous) and casts (asynchronous)
+ Calls
+ (synchronous) and
+ casts
originate from the corresponding API functions.
For calls the event contain whom to reply to.
- Type info originates from normal messages sent
- to the gen_statem process.
+ Type info originates from
+ regular process messages sent
+ to the gen_statem.
It is also possible for the state machine
implementation to insert events to itself,
in particular of types
@@ -421,7 +517,8 @@ erlang:'!' -----> Module:StateName/3
Transition options may be set by
actions
- and they modify how the state transition is done:
+ and they modify some details below in how
+ the state transition is done:
-
@@ -430,12 +527,12 @@ erlang:'!' -----> Module:StateName/3
are processed in order of appearance.
-
- If the
+ If
- transition_option()
+ postpone()
- postpone
- is true the current event is postponed.
+ is true
+ the current event is postponed.
-
If the state changes the queue of incoming events
@@ -450,15 +547,16 @@ erlang:'!' -----> Module:StateName/3
all other events.
-
- If the
-
- transition_option()
-
+ If a
- timeout
+ state_timeout()
+
+ is set through
+
+ action() timeout
- is set a state timer may be started or a timeout zero event
- may be enqueued as the newest incoming that is the last
+ a state timer may be started or a timeout zero event
+ may be enqueued as the newest incoming, that is the last
to process before going into receive for new events.
-
@@ -466,7 +564,12 @@ erlang:'!' -----> Module:StateName/3
state function
is called with the oldest enqueued event if there is any,
otherwise the gen_statem goes into receive
- or hibernation (if the option hibernate is true)
+ or hibernation
+ (if
+
+ hibernate()
+
+ is true)
to wait for the next message. In hibernation the next
non-system event awakens the gen_statem, or rather
the next incoming message awakens the gen_statem
@@ -518,7 +621,7 @@ erlang:'!' -----> Module:StateName/3
Also note that it is not possible nor needed
to cancel this timeout using the
- action() cancel_timer.
+ action() cancel_timer
since this timeout is cancelled automatically by any other event.
@@ -538,25 +641,22 @@ erlang:'!' -----> Module:StateName/3
Actions are executed in the containing list order.
The order matters for some actions such as next_event
- and reply_action(). The order can in peculiar cases
- matter for remove_event with
- EventPredicate versus other
- event removal actions.
+ and reply_action().
- The order also matters for actions that set
+ Actions that set
transition options
- since setting an option overrides any previous
- of the same kind, so the last in the containing list wins.
+ overrides any previous of the same kind,
+ so the last in the containing list wins.
postpone
-
Set the
- transition_option() postpone
+ transition_option() postpone()
for this state transition.
This action is ignored when returned from
@@ -569,7 +669,7 @@ erlang:'!' -----> Module:StateName/3
-
Set the
- transition_option() hibernate
+ transition_option() hibernate()
for this state transition.
@@ -577,7 +677,7 @@ erlang:'!' -----> Module:StateName/3
-
Set the
- transition_option() timeout
+ transition_option() state_timeout()
to Time with the
EventContent as Msg
@@ -594,7 +694,7 @@ erlang:'!' -----> Module:StateName/3
All next_event actions
in the containing list are buffered and inserted
after the actions have been done
- so the first in the list will be the first to process.
+ so that the first in the list will be the first to process.
An event of type
internal
@@ -612,7 +712,7 @@ erlang:'!' -----> Module:StateName/3
and postpone events in the same actions list
does not get into the event queue until after all actions
has been done so you can not remove an event that you insert
- in the same actions list. Make up your mind!
+ with the same actions list. Make up your mind!
cancel_timer
-
@@ -622,7 +722,7 @@ erlang:'!' -----> Module:StateName/3
with TimerRef,
clean the process message queue from any late timeout message,
- and removes any late timeout message
+ and remove any late timeout message
from the gen_statem event queue using
{remove_event,EventPredicate} above.
This is a convenience function that saves quite some
@@ -639,7 +739,7 @@ erlang:'!' -----> Module:StateName/3
unlink
-
- Like {cancel_timer,_} above but for
+ Like cancel_timer above but for
unlink/1
--
cgit v1.2.3