From 99d48f5921f95073c8e46280494746b0e1a2c375 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Tue, 16 Feb 2016 17:54:15 +0100 Subject: Implement option callback_mode --- lib/stdlib/doc/src/gen_statem.xml | 272 ++++++++++++++++++++++++-------------- 1 file changed, 172 insertions(+), 100 deletions(-) (limited to 'lib/stdlib/doc') diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index f7ce0f61ae..f608e10469 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -31,8 +31,15 @@ gen_statem Generic State Machine Behaviour -

A behaviour module for implementing a state machine, primarily - a finite state machine, but an infinite state space is possible. +

A behaviour module for implementing a state machine. + Two callback modes are supported. One for a finite state + machine like gen_fsm + that require the state to be an atom and use that state as + the name of the callback function for a particular state, + and one without restriction on the state that use the same + callback function for all states. +

+

A generic state machine process (gen_statem) implemented using this module will have a standard set of interface functions and include functionality for tracing and error reporting. @@ -55,7 +62,7 @@ gen_statem:stop -----> Module:terminate/2 gen_statem:call gen_statem:cast erlang:send -erlang:'!' -----> Module:State/5 +erlang:'!' -----> Module:StateName/5 Module:handle_event/5 - -----> Module:terminate/3 @@ -75,47 +82,39 @@ erlang:'!' -----> Module:State/5

The "state function" for a specific state in a gen_statem is the callback function that is called - for all events in this state. - An event can can be postponed causing it to be retried - after the state has changed, or more precisely; - after a state change all postponed events are retried - in the new state. + for all events in this state, and is selected depending on + callback_mode + that the implementation selects during gen_statem init.

-

The state machine - State - is normally an atom in which case the - state function - that will be called is - Module:State/5. - For a - State - that is not an atom the - state function - - Module:handle_event/5 - will be called. - If you use handle_event as a - state and later - decide to use non-atom states you will then have to - rewrite your code to stop using that state. +

When + callback_mode + is state_functions the state has to be an atom and + is used as the state function name. + See + + Module:StateName/5 + . + This naturally collects all code for a specific state + in one function and hence dispatches on state first.

-

When using an atom-only - State - it becomes fairly obvious in the implementation code - which events are handled in which state - since there are different callback functions for different states. +

When + callback_mode + is handle_event_function the state can be any term + and the state function name is + + Module:handle_event/5 + . + 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 + states so you do not accidentally postpone one event + forever creating an infinite busy loop.

- When using a non-atom State - all events are handled in the callback function - - Module:handle_event/5 - - so it may require more coding discipline to ensure what events - are handled in which state. Therefore it might be a wee bit - easier to accidentally postpone an event in two or more states - and not handling it in any of them causing a tight infinite - loop when the event bounces to be retried between the states. + An event can can be postponed causing it to be retried + after the state has changed, or more precisely; + after a state change all postponed events are retried + in the new state.

A gen_statem handles system messages as documented in sys. @@ -239,18 +238,22 @@ erlang:'!' -----> Module:State/5 -

If the gen_statem State is an atom(), the - state function is - Module:State/5. - If it is any other term() the - state function is - - Module:handle_event/5 - . After a state change (NewState =/= State) +

After a state change (NewState =/= State) all postponed events are retried.

+ + + +

If + + callback_mode + is state_functions, which is the default, + the state has to be of this type i.e an atom(). +

+
+
@@ -296,6 +299,33 @@ erlang:'!' -----> Module:State/5

+ + + +

Option that only is valid when initializing the gen_statem

+
+
+ + + + + state_functions + The state has to be of type + state_name() + and one callback function per state that is + + Module:StateName/5 + is used. This is the default. + + handle_event_function + The state can be any term and the callback function + + Module:handle_event/5 + is used for all states. + + + + @@ -472,6 +502,53 @@ erlang:'!' -----> Module:State/5 + + + + + + {stop,Reason,NewStateData} + + The same as + {stop,Reason,[],NewStateData} + + {stop, + Reason, + Replies, + NewStateData} + + The gen_statem will first send all + Replies and then terminate by calling + + Module:terminate/3 + with Reason. + + + + {next_state,NewState,NewStateData} + + + The same as + + {next_state,NewState,NewStateData,[]} + + + + + {next_state, + NewState, + NewStateData, + StateOps} + + + The gen_statem will do a state transition to + NewState + (which may be the same as State) + and execute all StateOps + + + + @@ -777,13 +854,16 @@ erlang:'!' -----> Module:State/5

Module, Options and Server have the same meanings as when calling - gen_statem:start[_link]/3,4. + + gen_statem:start[_link]/3,4 + . However, the server_name() name must have been registered accordingly before this function is called.

-

State and StateData +

State, StateData + and StateOps have the same meanings as in the return value of Module:init/1. Also, the callback module Module @@ -821,8 +901,13 @@ erlang:'!' -----> Module:State/5  | {ok,State,StateData,StateOps}  | {stop,Reason} | ignore State = state() - StateData = state_data() - StateOps = [state_op()] + StateData = + state_data() + + StateOps = + [state_op() + | init_option()] + Reason = term() @@ -843,10 +928,15 @@ erlang:'!' -----> Module:State/5 of the gen_statem.

The StateOps - are executed before entering the first + are executed when entering the first state just as for a state function.

+

This function allows an option to select the callback mode + of the gen_statem. See + init_option. + This option is not allowed from the state function(s). +

If something goes wrong during the initialization the function should return {stop,Reason} or ignore. See @@ -856,10 +946,10 @@ erlang:'!' -----> Module:State/5 - Module:handle_event(EventType, EventContent, - PrevState, State, StateData) -> Result + Module:StateName(EventType, EventContent, + PrevStateName, StateName, StateData) -> Result - Module:State(EventType, EventContent, + Module:handle_event(EventType, EventContent, PrevState, State, StateData) -> Result Handle an event @@ -868,41 +958,23 @@ erlang:'!' -----> Module:State/5 event_type() EventContent = term() - Result = {stop,Reason,NewStateData} -   The same as {stop,Reason,[],NewStateData} -   | {stop,Reason,Reply,NewStateData} -   The same as - {stop,Reason,[Reply],NewStateData} - -   | {stop,Reason,Replies,NewStateData} -   The gen_statem will first send all - Replies and then call - - Module:terminate/3 - with Reason and terminate. - -   | {next_state,NewState,NewStateData} -   The same as - {next_state,NewState,NewStateData,NewStateData,[]} - -   | {next_state,NewState,NewStateData,StateOps} -   The gen_statem will do a state transition to - NewState (which may be the same as State) - and execute all StateOps - - Reason = term() - PrevState = State = NewState = + PrevStateName = + state_name() + | reference() + + StateName = + state_name() + + PrevState = State = state() StateData = NewStateData = state_data() - Reply = - reply_operation() - - Replies = [Reply] - StateOps = - [state_op()] + Result = + + state_callback_result() + @@ -923,19 +995,18 @@ erlang:'!' -----> Module:State/5 gen_statem:reply(Client, Reply) .

-

State - is the internal state of the gen_statem which - when State is an atom() - is the same as this function's name, so it is seldom useful, - except for example when comparing with PrevState - that is the gen_statem's previous state, or in - - Module:handle_event/5 - since that function is common for all states - that are not an atom(). -

-

If this function returns with - NewState =/= State +

StateName is rarely useful. One exception is if + you call a common event handling function from your state + function then you might want to pass StateName. +

+

PrevStateName and PrevState are rarely useful. + They can be used when you want to do something only at the + first event in a state. Note that when gen_statem enters + its first state this is set to a reference() since + that can not match the state. +

+

If this function returns with a new state that + does not match equal (=/=) to the current state all postponed events will be retried in the new state.

See state_op() @@ -946,7 +1017,7 @@ erlang:'!' -----> Module:State/5 - Module:terminate(Reason, State, StateData) + Module:terminate(Reason, State, StateData) -> Ignored Clean up before termination Reason = normal | shutdown | {shutdown,term()} | term() @@ -956,6 +1027,7 @@ erlang:'!' -----> Module:State/5 state_data() + Ignored = term()

This function is called by a gen_statem when it is about to -- cgit v1.2.3