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(-) (limited to 'lib/stdlib/doc/src/gen_statem.xml') 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