From 17405463ba134e71ff09e8d2921de9aa931805ee Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 9 May 2016 14:20:03 +0200 Subject: Fix all seealso and other minor changes --- system/doc/design_principles/statem.xml | 184 ++++++++++---------------------- 1 file changed, 57 insertions(+), 127 deletions(-) (limited to 'system') diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml index 8b0fbed7c0..585b1a35f5 100644 --- a/system/doc/design_principles/statem.xml +++ b/system/doc/design_principles/statem.xml @@ -84,8 +84,9 @@ State(S) x Event(E) -> Actions(A), State(S') a server Data besides the state. Because of this, and as there is no restriction on the number of states (assuming that there is enough virtual machine memory) - or on the number of distinct input events, makes - a state machine implemented with this behavior Turing complete. + or on the number of distinct input events, + a state machine implemented with this behavior + is in fact Turing complete. But it feels mostly like an Event-Driven Mealy Machine.

@@ -101,8 +102,8 @@ State(S) x Event(E) -> Actions(A), State(S')

- In mode - state_functions, + In mode + state_functions, the state transition rules are written as some Erlang functions, which conform to the following convention:

@@ -113,20 +114,19 @@ StateName(EventType, EventContent, Data) ->

- In mode - handle_event_function, + In mode + handle_event_function, only one Erlang function provides all state transition rules:

 handle_event(EventType, EventContent, State, Data) ->
     .. code for actions here ...
-    {next_state, State', Data'}
+ {next_state, NewState, NewData}

Both these modes allow other return tuples; see - - Module:StateName/3 + Module:StateName/3 in the gen_statem manual page. These other return tuples can, for example, stop the machine, execute state transition actions on the machine engine itself, @@ -172,8 +172,7 @@ handle_event(EventType, EventContent, State, Data) -> This mode works equally well when you want to focus on one event at the time or on one state at the time, but function - - Module:handle_event/4 + Module:handle_event/4 quickly grows too large to handle without introducing dispatching.

@@ -291,9 +290,7 @@ start_link(Code) -> ]]>

start_link calls function - - gen_statem:start_link/4 - , + gen_statem:start_link/4, which spawns and links to a new process, a gen_statem.

@@ -308,9 +305,7 @@ start_link(Code) -> Instead its pid must be used. The name can also be specified as {global,Name}, then the gen_statem is registered using - - global:register_name/2 - + global:register_name/2 in Kernel.

@@ -339,9 +334,7 @@ start_link(Code) ->

The fourth argument, [], is a list of options. For the available options, see - - gen_statem:start_link/3 - . + gen_statem:start_link/3.

@@ -350,13 +343,9 @@ start_link(Code) -> calls callback function code_lock:init(Code). This function is expected to return {CallbackMode,State,Data}, where - - CallbackMode - + CallbackMode selects callback module state function mode, in this case - - state_functions - + state_functions through macro ?CALLBACK_MODE. That is, each state has got its own handler function. State is the initial state of the gen_statem, @@ -375,23 +364,17 @@ init(Code) -> {?CALLBACK_MODE,locked,Data}. ]]>

Function - - gen_statem:start_link - + gen_statem:start_link is synchronous. It does not return until the gen_statem is initialized and is ready to receive events.

Function - - gen_statem:start_link - + gen_statem:start_link must be used if the gen_statem is part of a supervision tree, that is, started by a supervisor. Another function, - - gen_statem:start - + gen_statem:start can be used to start a standalone gen_statem, that is, a gen_statem that is not part of a supervision tree.

@@ -403,9 +386,7 @@ init(Code) -> Handling Events

The function notifying the code lock about a button event is implemented using - - gen_statem:cast/2: - + gen_statem:cast/2:

@@ -528,9 +509,7 @@ handle_event({call,From}, code_length, #{code := Code} = Data) -> ]]>

This example uses - - gen_statem:call/2 - , + gen_statem:call/2, which waits for a reply from the server. The reply is sent with a {reply,From,Reply} tuple in an action list in the {keep_state,...} tuple @@ -545,15 +524,13 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->

If mode handle_event_function is used, all events are handled in - - Module:handle_event/4 - + Module:handle_event/4 and we can (but do not have to) use an event-centered approach where we dispatch on event first and then state:

strategy must be a time-out value and the gen_statem must in function init/1 set itself to trap exit signals by calling - - process_flag(trap_exit, true) - . - When ordered to shut down, the gen_statem then calls - callback function terminate(shutdown, State, Data): + process_flag(trap_exit, true):

@@ -616,6 +589,10 @@ init(Args) -> do_lock(), ... ]]> +

+ When ordered to shut down, the gen_statem then calls + callback function terminate(shutdown, State, Data). +

In the following example, function terminate/3 locks the door if it is open, so we do not accidentally leave the door @@ -633,9 +610,7 @@ terminate(_Reason, State, _Data) ->

If the gen_statem is not part of a supervision tree, it can be stopped using - - gen_statem:stop - , + gen_statem:stop, preferably through an API function:

Actions

In the first sections actions were mentioned as a part of - the general state machine model. These actions + the general state machine model. These general actions are implemented with the code that callback module gen_statem executes in an event-handling callback function before returning @@ -671,17 +646,11 @@ stop() -> that a callback function can order the gen_statem engine to do after the callback function return. These are ordered by returning a list of - - actions - + actions in the - - return tuple - + return tuple from the - - callback function - . + callback function. These state transition actions affect the gen_statem engine itself and can do the following:

@@ -697,9 +666,7 @@ stop() -> and replying to a caller. An example of event postponing is included later in this chapter. For details, see the - - gen_statem(3) - + gen_statem(3) manual page. You can, for example, reply to many callers and generate multiple next events to handle. @@ -712,9 +679,7 @@ stop() -> Event Types

The previous sections mentioned a few - - event types - . + event types. Events of all types are handled in the same callback function, for a given state, and the function gets EventType and EventContent as arguments. @@ -727,22 +692,16 @@ stop() -> cast Generated by - - gen_statem:cast. - + gen_statem:cast. {call,From} Generated by - - gen_statem:call - , + gen_statem:call, where From is the reply address to use when replying either through the state transition action {reply,From,Msg} or by calling - - gen_statem:reply - . + gen_statem:reply. info @@ -759,7 +718,7 @@ stop() -> Generated by state transition action {next_event,internal,EventContent}. - All event types above can be generated using + All event types above can also be generated using {next_event,EventType,EventContent}. @@ -780,9 +739,7 @@ stop() -> or you want to start a timer in one state and respond to the time-out in another. This can be accomplished with a regular Erlang timer: - - erlang:start_timer. - + erlang:start_timer.

For the example so far in this chapter: using the @@ -818,9 +775,7 @@ open(cast, {button,_}, Data) -> ]]>

If you need to cancel a timer because of some other event, you can use - - erlang:cancel_timer(Tref) - . + erlang:cancel_timer(Tref). Notice that a time-out message cannot arrive after this, unless you have postponed it (see the next section) before, so ensure that you do not accidentally postpone such messages. @@ -844,9 +799,7 @@ open(cast, {button,_}, Data) ->

Postponing is ordered by the state transition - - action - + action postpone.

@@ -861,7 +814,7 @@ open(cast, {button,_}, Data) -> ... ]]>

- A postponed event is only retried after a state change + The fact that a postponed event is only retried after a state change translates into a requirement on the event and state space. If you have a choice between storing a state data item in the State or in the Data: @@ -953,9 +906,7 @@ do_unlock() ->

The state transition - - action - + action postpone is designed to model selective receives. A selective receive implicitly postpones any not received events, but the postpone @@ -977,16 +928,12 @@ do_unlock() -> It can sometimes be beneficial to be able to generate events to your own state machine. This can be done with the state transition - - action - + action {next_event,EventType,EventContent}.

You can generate events of any existing - - type - , + type, but the internal type can only be generated through action next_event. Hence, it cannot come from an external source, so you can be certain that an internal event is an event @@ -1150,9 +1097,9 @@ code_change(_Vsn, State, Data, _Extra) ->

This section describes what to change in the example to use one handle_event/4 function. - The following clean first-dispatch-on-event approach - does not work that well because of the generated - entry actions: + The previously used clean first-dispatch-on-event approach + does not work that well here because of the generated + entry actions so this example dispatches on state first:

To avoid this, you can format the internal state that gets in the error log and gets returned from - - sys:get_status/1,2 - + sys:get_status/1,2 by implementing function - - Module:format_status/2 - , + Module:format_status/2, for example like this:

]]>

It is not mandatory to implement a - - Module:format_status/2 - + Module:format_status/2 function. If you do not, a default implementation is used that does the same as this example function without filtering the Data term, that is, StateData = {State,Data}. @@ -1274,13 +1215,9 @@ format_status(Opt, [_PDict,State,Data]) -> Complex State

The callback mode - - handle_event_function - + handle_event_function enables using a non-atom state as described in section - - Callback Modes - , + Callback Modes, for example, a complex state term like a tuple.

@@ -1308,8 +1245,7 @@ format_status(Opt, [_PDict,State,Data]) ->

So we make the button/1 function synchronous by using - - gen_statem:call + gen_statem:call and still postpone its events in the open state. Then a call to button/1 during the open state does not return until the state transits to locked, @@ -1462,16 +1398,12 @@ format_status(Opt, [_PDict,State,Data]) -> and the amount of heap memory all these servers need is a problem, then the memory footprint of a server can be mimimized by hibernating it through - - proc_lib:hibernate/3. - + proc_lib:hibernate/3.

It is rather costly to hibernate a process; see - - erlang:hibernate/3 - . + erlang:hibernate/3. It is not something you want to do after every event.

@@ -1495,9 +1427,7 @@ handle_event( ]]>

The - - [hibernate] - + [hibernate] action list on the last line when entering the {open,_} state is the only change. If any event arrives in the {open,_}, state, we -- cgit v1.2.3