From 26b3c7d60d52d8a7be006b06d856bb0f7276e77a Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 21 Apr 2016 15:58:52 +0200 Subject: Modify code_change/4 to return CallbackMode Also move check of non-atom states in callback mode state_functions to where the state function is called. This gives homogenous diagnostics for state functions, code_change/4 and system_replace_state StateFun. Irregularities pointed out by James Fish. --- system/doc/design_principles/statem.xml | 87 +++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 38 deletions(-) (limited to 'system/doc/design_principles/statem.xml') diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml index 72aaafd780..27b9b7c761 100644 --- a/system/doc/design_principles/statem.xml +++ b/system/doc/design_principles/statem.xml @@ -90,11 +90,11 @@ State(S) x Event(E) -> Actions(A), State(S') Callback Modes

- The gen_statem behaviour supports two different - - callback modes. + The gen_statem behaviour supports two different callback modes. + In the mode + + state_functions, - In the mode state_functions, the state transition rules are written as a number of Erlang functions, which conform to the following convention:

@@ -103,7 +103,11 @@ StateName(EventType, EventContent, Data) -> .. code for actions here ... {next_state, NewStateName, NewData}.

- In the mode handle_event_function there is only one + In the mode + + handle_event_function + + there is only one Erlang function that implements all state transition rules:

@@ -125,9 +129,7 @@ handle_event(EventType, EventContent, State, Data) ->
       Choosing Callback Mode
       

The two - - callback modes - + callback modes gives different possibilities and restrictions, but one goal remains: you want to handle all possible combinations of @@ -186,7 +188,7 @@ handle_event(EventType, EventContent, State, Data) -> Example

This is an example starting off as equivalent to the the example in the - gen_fsm behaviour + gen_fsm behaviour description. In later chapters additions and tweaks are made using features in gen_statem that gen_fsm does not have. At the end of this section you can find the example again @@ -217,6 +219,7 @@ handle_event(EventType, EventContent, State, Data) -> -module(code_lock). -behaviour(gen_statem). -define(NAME, code_lock). +-define(CALLBACK_MODE, state_functions). -export([start_link/1]). -export([button/1]). @@ -233,7 +236,7 @@ button(Digit) -> init(Code) -> do_lock(), Data = #{code => Code, remaining => Code}, - {state_functions,locked,Data}. + {?CALLBACK_MODE,locked,Data}. locked( cast, {button,Digit}, @@ -264,7 +267,7 @@ terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. code_change(_Vsn, State, Data, _Extra) -> - {ok,State,Data}. + {?CALLBACK_MODE,State,Data}. ]]>

The code is explained in the next sections.

@@ -340,16 +343,20 @@ start_link(Code) -> If name registration succeeds, the new gen_statem process calls the callback function code_lock:init(Code). This function is expected to return {CallbackMode,State,Data}, - where CallbackMode selects callback module state function - mode, in this case state_functions that is each state + where + + CallbackMode + + selects callback module state function mode, in this case + + state_functions + + through the macro ?CALLBACK_MODE that is; each state has got its own handler function. State is the initial state of the gen_statem, in this case locked; assuming the door is locked to begin with. Data is the internal server data of the gen_statem. - Here the server data is a - - map - + Here the server data is a map with the key code that stores the correct button sequence and the key remaining that stores the remaining correct button sequence @@ -359,7 +366,7 @@ start_link(Code) -> init(Code) -> do_lock(), Data = #{code => Code, remaining => Code}, - {state_functions,locked,Data}. + {?CALLBACK_MODE,locked,Data}. ]]>

@@ -536,13 +543,12 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->

- Data = #{code => Code, remaining => Code}, - {handle_event_function,locked,Data}. +... handle_event(cast, {button,Digit}, State, #{code := Code} = Data) -> case State of @@ -598,8 +604,8 @@ handle_event(timeout, _, open, Data) -> process_flag(trap_exit, true), + do_lock(), ... - {CallbackMode,State,Data}. ]]>

In this example we let the terminate/3 function @@ -994,11 +1000,18 @@ do_unlock() -> utilizing a helper function enter/3 for state entry:

+ process_flag(trap_exit, true), Data = #{code => Code}, - enter(state_functions, locked, Data). + enter(?CALLBACK_MODE, locked, Data). ... + locked(internal, enter, _Data) -> do_lock(), {keep_state,Data#{remaining => Code}}; @@ -1053,6 +1066,7 @@ enter(Tag, State, Data) -> -module(code_lock). -behaviour(gen_statem). -define(NAME, code_lock_2). +-define(CALLBACK_MODE, state_functions). -export([start_link/1,stop/0]). -export([button/1,code_length/0]). @@ -1070,8 +1084,9 @@ code_length() -> gen_statem:call(?NAME, code_length). init(Code) -> + process_flag(trap_exit, true), Data = #{code => Code}, - enter(state_functions, locked, Data). + enter(?CALLBACK_MODE, locked, Data). locked(internal, enter, #{code := Code} = Data) -> do_lock(), @@ -1115,7 +1130,7 @@ terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. code_change(_Vsn, State, Data, _Extra) -> - {ok,State,Data}. + {?CALLBACK_MODE,State,Data}. ]]> @@ -1129,14 +1144,10 @@ code_change(_Vsn, State, Data, _Extra) ->

- process_flag(trap_exit, true), - Data = #{code => Code}, - enter(handle_event_function, locked, Data). +-export([handle_event/4]). ... @@ -1185,11 +1196,10 @@ handle_event({call,From}, code_length, _State, #{code := Code}) ->
Complex State

- The - - callback mode + The callback mode + + handle_event_function - handle_event_function enables using a non-atom state as described in Callback Modes, @@ -1245,6 +1255,7 @@ handle_event({call,From}, code_length, _State, #{code := Code}) -> -module(code_lock). -behaviour(gen_statem). -define(NAME, code_lock_3). +-define(CALLBACK_MODE, handle_event_function). -export([start_link/2,stop/0]). -export([button/1,code_length/0,set_lock_button/1]). @@ -1267,7 +1278,7 @@ set_lock_button(LockButton) -> init({Code,LockButton}) -> process_flag(trap_exit, true), Data = #{code => Code}, - enter(handle_event_function, {locked,LockButton}, Data, []). + enter(?CALLBACK_MODE, {locked,LockButton}, Data, []). %% State: locked handle_event(internal, enter, {locked,_}, #{code := Code} = Data) -> @@ -1323,7 +1334,7 @@ terminate(_Reason, State, _Data) -> State =/= locked andalso do_lock(), ok. code_change(_Vsn, State, Data, _Extra) -> - {ok,State,Data}. + {?CALLBACK_MODE,State,Data}. ]]>

It may be an ill-fitting model for a physical code lock -- cgit v1.2.3