From 09d138e229846b7056331151135b7c8a52dc476f Mon Sep 17 00:00:00 2001 From: xsipewe Date: Fri, 6 May 2016 09:55:25 +0200 Subject: Editorial update --- lib/stdlib/doc/src/gen_statem.xml | 1723 ++++++++++++++++--------------- system/doc/design_principles/statem.xml | 696 ++++++------- 2 files changed, 1244 insertions(+), 1175 deletions(-) diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index ec7f267c64..4fe940bd05 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -29,39 +29,49 @@ gen_statem - Generic State Machine Behaviour + Generic state machine behavior.

- A behaviour module for implementing a state machine. Two + This behavior module provides a state machine. Two callback modes - are supported. One for finite state machines - (gen_fsm like) - that requires the state to be an atom and uses that state as - the name of the current callback function, - and one without restriction on the state data type - that uses one callback function for all states. -

-

- This is a new behaviour in OTP-19.0. - It has been thoroughly reviewed, is stable enough - to be used by at least two heavy OTP applications, and is here to stay. - But depending on user feedback, we do not expect - but might find it necessary to make minor - not backwards compatible changes into OTP-20.0, - so its state can be designated as "not quite experimental"... + are supported:

+ + +

One for finite-state machines + (gen_fsm like), + which requires the state to be an atom and uses that state as + the name of the current callback function +

+
+ +

One without restriction on the state data type + that uses one callback function for all states +

+
+
+ +

+ This is a new behavior in Erlang/OTP 19.0. + It has been thoroughly reviewed, is stable enough + to be used by at least two heavy OTP applications, and is here to stay. + Depending on user feedback, we do not expect + but can find it necessary to make minor + not backward compatible changes into Erlang/OTP 20.0. +

+

- The gen_statem behaviour is intended to replace + The gen_statem behavior is intended to replace gen_fsm for new code. - It has the same features and add some really useful: + It has the same features and adds some really useful:

- State code is gathered - The state can be any term - Events can be postponed - Events can be self generated - A reply can be sent from a later state - There can be multiple sys traceable replies + State code is gathered. + The state can be any term. + Events can be postponed. + Events can be self-generated. + A reply can be sent from a later state. + There can be multiple sys traceable replies.

The callback model(s) for gen_statem differs from @@ -71,19 +81,18 @@

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. - It will also fit into an OTP supervision tree. Refer to + using this module has a standard set of interface functions + and includes functionality for tracing and error reporting. + It also fits into an OTP supervision tree. For more information, see OTP Design Principles - - for more information. + .

A gen_statem assumes all specific parts to be located in a - callback module exporting a pre-defined set of functions. - The relationship between the behaviour functions and the callback - functions can be illustrated as follows:

+ callback module exporting a predefined set of functions. + The relationship between the behavior functions and the callback + functions is as follows:

 gen_statem module            Callback module
 -----------------            ---------------
@@ -103,37 +112,37 @@ erlang:'!'            -----> Module:StateName/3
 -                     -----> Module:code_change/4

Events are of different - types + types, so the callback functions can know the origin of an event and how to respond.

If a callback function fails or returns a bad value, - the gen_statem will terminate. An exception of class - throw, - however, is not regarded as an error but as a valid return. + the gen_statem terminates. However, an exception of class + throw + is not regarded as an error but as a valid return.

- +

The "state function" for a specific state in a gen_statem is the callback function that is called - for all events in this state, and is selected depending on which + for all events in this state. It is selected depending on which callback mode - that the implementation specifies when the the server starts. + that the implementation specifies when the server starts.

When the callback mode - is state_functions, the state has to be an atom and - is used as the state function name. See + is state_functions, the state must be an atom and + is used as the state function name; see 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 fact that there is - a mandatory callback function + Notice that in this mode + the mandatory callback function Module:terminate/3 makes the state name terminate unusable. @@ -141,21 +150,21 @@ erlang:'!' -----> Module:StateName/3

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

The gen_statem enqueues incoming events in order of arrival and presents these to the state function - in that order. The state function can postpone an event + in that order. The state function can postpone an event so it is not retried in the current state. After a state change the queue restarts with the postponed events.

@@ -163,7 +172,7 @@ erlang:'!' -----> Module:StateName/3 The gen_statem event queue model is sufficient 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 + in a receive statement, and changing states corresponds to entering a new receive statement.

@@ -173,73 +182,76 @@ erlang:'!' -----> Module:StateName/3 action() next_event and such an event is inserted as the next to present - to the state function. That is: as if it is - the oldest incoming event. There is a dedicated + to the state function. That is, as if it is + the oldest incoming event. A dedicated event_type() - internal that can be used for such events making them impossible + internal can be used for such events making them impossible to mistake for external events.

Inserting an event replaces the trick of calling your own state handling functions that you often would have to - resort to in for example gen_fsm + resort to in, for example, + gen_fsm to force processing an inserted event before others. - A warning, though: if you in gen_statem for example - postpone an event in one state and then call some other state function of yours, - you have not changed states and hence the postponed event will not be retried, - which is logical but might be confusing.

+ +

If you in gen_statem, for example, postpone + an event in one state and then call another state function + of yours, you have not changed states and hence the postponed event + is not retried, which is logical but can be confusing. +

+

- See the type + For the details of a state transition, see type - transition_option() - - for the details of a state transition. + transition_option() + .

- A gen_statem handles system messages as documented in + A gen_statem handles system messages as described in sys. The sysmodule can be used for debugging a gen_statem.

- Note that a gen_statem does not trap exit signals + Notice that a gen_statem does not trap exit signals automatically, this must be explicitly initiated in the callback module (by calling - process_flag(trap_exit, true). + process_flag(trap_exit, true).

Unless otherwise stated, all functions in this module fail if the specified gen_statem does not exist or - if bad arguments are given. + if bad arguments are specified.

The gen_statem process can go into hibernation; see - proc_lib:hibernate/3. - + proc_lib:hibernate/3 + . It is done when a state function or Module:init/1 specifies hibernate in the returned Actions - list. This feature might be useful to reclaim process heap memory + list. This feature can be useful to reclaim process heap memory while the server is expected to be idle for a long time. - However, use this feature with care - since hibernation can be too costly + However, use this feature with care, + as hibernation can be too costly to use after every event; see - erlang:hibernate/3. - + erlang:hibernate/3 + .

- EXAMPLE + Example

- This example shows a simple pushbutton model + The following example shows a simple pushbutton model for a toggling pushbutton implemented with callback mode state_functions. @@ -247,7 +259,8 @@ erlang:'!' -----> Module:StateName/3 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:

+

The following is the complete callback module file + pushbutton.erl:

-module(pushbutton). -behaviour(gen_statem). @@ -305,7 +318,7 @@ handle_event(_, _, Data) -> %% Ignore all other events {keep_state,Data}. -

And this is a shell session when running it:

+

The following is a shell session when running it:

 1> pushbutton:start().
 {ok,<0.36.0>}
@@ -326,12 +339,11 @@ ok
      in function  gen:do_for_proc/2 (gen.erl, line 261)
      in call from gen_statem:call/3 (gen_statem.erl, line 386)
     
-

- And just to compare styles here is the same example using + To compare styles, here follows the same example using callback mode - state_functions, or rather here is code to replace - from the init/1 function of the pushbutton.erl + state_functions, or rather the code to replace + from function init/1 of the pushbutton.erl example file above:

@@ -364,11 +376,11 @@ handle_event(_, _, State, Data) -> - +

Name specification to use when starting - a gen_statem server. See + a gen_statem server. See start_link/3 @@ -380,7 +392,7 @@ handle_event(_, _, State, Data) -> - +

Server specification to use when addressing @@ -393,75 +405,83 @@ handle_event(_, _, State, Data) ->

It can be:

- pid()
- LocalName
- The gen_statem is locally registered. + pid() | LocalName + +

+ The gen_statem is locally registered. +

+
Name, Node - The gen_statem is locally registered - on another node. +

+ The gen_statem is locally registered + on another node. +

GlobalName - The gen_statem is globally registered - in global. +

+ The gen_statem is globally registered + in kernel:global. +

RegMod, ViaName - The gen_statem is registered through - an alternative process registry. - The registry callback module RegMod - should export the functions - register_name/2, unregister_name/1, - whereis_name/1 and send/2, - which should behave like the corresponding functions - in global. - Thus, {via,global,GlobalName} is the same as - {global,GlobalName}. +

+ The gen_statem is registered through + an alternative process registry. + The registry callback module RegMod + is to export functions + register_name/2, unregister_name/1, + whereis_name/1, and send/2, + which are to behave like the corresponding functions + in kernel:global. + Thus, {via,global,GlobalName} is the same as + {global,GlobalName}. +

- +

Debug option that can be used when starting - a gen_statem server through for example + a gen_statem server through, for example, enter_loop/5.

- For every entry in Dbgs + For every entry in Dbgs, the corresponding function in - sys will be called. + sys is called.

- +

Options that can be used when starting - a gen_statem server through for example + a gen_statem server through, for example, start_link/3.

- +

- Return value from the start functions for_example + Return value from the start functions, for example, start_link/3.

- - +

- Destination to use when replying through for example the + Destination to use when replying through, for example, the action() @@ -472,178 +492,194 @@ handle_event(_, _, State, Data) -> - +

- After a state change (NextState =/= State) + After a state change (NextState =/= State), all postponed events are retried.

- +

If the callback mode is state_functions, - the state has to be of this type. + the state must be of this type.

- +

A term in which the state machine implementation - should store any server data it needs. The difference between + is to store any server data it needs. The difference between this and the state() itself is that a change in this data does not cause - postponed events to be retried. Hence if a change + postponed events to be retried. Hence, if a change in this data would change the set of events that - are handled than that data item should be made + are handled, then that data item is to be made a part of the state.

- +

- External events are of 3 different type: - {call,From}, cast or info. + External events are of three types: + {call,From}, cast, or info. Calls (synchronous) and casts originate from the corresponding API functions. - For calls the event contain whom to reply to. + For calls, the event contains whom to reply to. Type info originates from regular process messages sent - to the gen_statem. It is also possible for the state machine - implementation to generate events of types + to the gen_statem. Also, the state machine + implementation can generate events of types timeout and internal to itself.

- +

The callback mode is selected when starting the gen_statem using the return value from Module:init/1 or when calling - enter_loop/5-7, + enter_loop/5,6,7, and with the return value from - Module:code_change/4. - + Module:code_change/4 + .

state_functions - The state has to be of type - state_name() - and one callback function per state that is - - Module:StateName/3 - - is used. +

+ The state must be of type + state_name() + and one callback function per state, that is, + + Module:StateName/3 + , + is used. +

handle_event_function - The state can be any term and the callback function - - Module:handle_event/4 - - is used for all states. +

+ The state can be any term and the callback function + + Module:handle_event/4 + + is used for all states. +

- +

- Transition options may be set by + Transition options can be set by actions - and they modify some details below in how + and they modify the following in how the state transition is done:

- All - actions - are processed in order of appearance. +

+ All + actions + are processed in order of appearance. +

- If - - postpone() - - is true - the current event is postponed. +

+ If + + postpone() + + is true, + the current event is postponed. +

- If the state changes the queue of incoming events - is reset to start with the oldest postponed. +

+ If the state changes, the queue of incoming events + is reset to start with the oldest postponed. +

- All events stored with - - action() - - next_event - are inserted in the queue to be processed before - all other events. +

+ All events stored with + + action() + + next_event + are inserted in the queue to be processed before + all other events. +

- If an - - event_timeout() - - is set through - - action() - - timeout - an event timer may be started or a timeout zero event - may be enqueued. +

+ If an + + event_timeout() + + is set through + + action() + + timeout, + an event timer can be started or a time-out zero event + can be enqueued. +

- The (possibly new) - state function - is called with the oldest enqueued event if there is any, - otherwise the gen_statem goes into receive - 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 - but if it is a system event - it goes right back into hibernation. +

+ The (possibly new) + state function + is called with the oldest enqueued event if there is any, + otherwise the gen_statem goes into receive + 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, + but if it is a system event + it goes right back into hibernation. +

- +

- If true postpone the current event and retry + If true, postpones the current event and retries it when the state changes (NextState =/= State).

- +

- If true hibernate the gen_statem + If true, hibernates the gen_statem by calling proc_lib:hibernate/3 @@ -651,9 +687,9 @@ handle_event(_, _, State, Data) -> before going into receive to wait for a new external event. If there are enqueued events, - to prevent receiving any new event; a + to prevent receiving any new event, an - garbage_collect/0 + erlang:garbage_collect/0 is done instead to simulate that the gen_statem entered hibernation and immediately got awakened by the oldest enqueued event. @@ -661,39 +697,39 @@ handle_event(_, _, State, Data) -> - +

- Generate an event of + Generates an event of event_type() timeout - after this time (in milliseconds) unless some other - event arrives in which case this timeout is cancelled. - Note that a retried or inserted event - counts just like a new in this respect. + after this time (in milliseconds) unless another + event arrives in which case this time-out is cancelled. + Notice that a retried or inserted event + counts like a new in this respect.

- If the value is infinity no timer is started since - it will never trigger anyway. + If the value is infinity, no timer is started, as + it never triggers anyway.

- If the value is 0 the timeout event is immediately enqueued - unless there already are enqueued events since then the - timeout is immediately cancelled. - This is a feature ensuring that a timeout 0 event - will be processed before any not yet received external event. + If the value is 0, the time-out event is immediately enqueued + unless there already are enqueued events, as the + time-out is then immediately cancelled. + This is a feature ensuring that a time-out 0 event + is processed before any not yet received external event.

- Note that it is not possible nor needed to cancel this timeout - since it is cancelled automatically by any other event. + Notice that it is not possible or needed to cancel this time-out, + as it is cancelled automatically by any other event.

- +

- These state transition actions may be invoked by + These state transition actions can be invoked by returning them from the state function, from Module:init/1 @@ -708,9 +744,9 @@ handle_event(_, _, State, Data) -> transition options - overrides any previous of the same type, + override any previous of the same type, so the last in the containing list wins. - For example the last + For example, the last event_timeout() @@ -719,77 +755,87 @@ handle_event(_, _, State, Data) -> postpone - Set the - - transition_option() - - - postpone() - - for this state transition. - This action is ignored when returned from - Module:init/1 - or given to - enter_loop/5,6 - since there is no event to postpone in those cases. +

+ Sets the + + transition_option() + + + postpone() + + for this state transition. + This action is ignored when returned from + Module:init/1 + or given to + enter_loop/5,6, + as there is no event to postpone in those cases. +

hibernate - Set the - - transition_option() - - - hibernate() - - for this state transition. +

+ Sets the + + transition_option() + + + hibernate() + + for this state transition. +

Timeout - Short for {timeout,Timeout,Timeout} that is - the timeout message is the timeout time. - This form exists to make the - state function - return value {next_state,NextState,NewData,Timeout} - allowed like for - - gen_fsm Module:StateName/2. - +

+ Short for {timeout,Timeout,Timeout}, that is, + the time-out message is the time-out time. + This form exists to make the + state function + return value {next_state,NextState,NewData,Timeout} + allowed like for + + gen_fsm:Module:StateName/2 + . +

timeout - Set the - - transition_option() - - - event_timeout() - - to Time with EventContent. +

+ Sets the + + transition_option() + + + event_timeout() + + to Time with EventContent. +

reply_action() - Reply to a caller. + +

+ Replies to a caller. +

+
next_event - Store the given EventType +

+ Stores the specified EventType and EventContent for insertion after all actions have been executed. - - +

The stored events are inserted in the queue as the next to process - before any already queued events. The order of these stored events - is preserved so the first next_event in the containing list - will become the first to process. + before any already queued events. The order of these stored events + is preserved, so the first next_event in the containing + list becomes the first to process.

-
-

An event of type internal - should be used when you want to reliably distinguish + is to be used when you want to reliably distinguish an event inserted this way from any external event.

@@ -797,294 +843,125 @@ handle_event(_, _, State, Data) ->
- +

- Reply to a caller waiting for a reply in + Replies to a caller waiting for a reply in call/2. - From must be the term from the + From must be the term from argument {call,From} - argument to the + to the state function.

- + next_state - The gen_statem will do a state transition to - NextStateName - (which may be the same as the current state), - set NewData - and execute all Actions +

+ The gen_statem does a state transition to + NextStateName + (which can be the same as the current state), + sets NewData, + and executes all Actions. +

All these terms are tuples or atoms and this property - will hold in any future version of gen_statem, - just in case you need such a promise. + will hold in any future version of gen_statem.

- + next_state - The gen_statem will do a state transition to - NextState - (which may be the same as the current state), - set NewData - and execute all Actions +

+ The gen_statem does a state transition to + NextState + (which can be the same as the current state), + sets NewData, + and executes all Actions. +

All these terms are tuples or atoms and this property - will hold in any future version of gen_statem, - just in case you need such a promise. + will hold in any future version of gen_statem.

- + stop - Terminate the gen_statem by calling - - Module:terminate/3 - - with Reason and - NewData, if given. +

+ Terminates the gen_statem by calling + + Module:terminate/3 + + with Reason and + NewData, if specified. +

stop_and_reply - Send all Replies - then terminate the gen_statem by calling - - Module:terminate/3 - - with Reason and - NewData, if given. +

+ Sends all Replies, + then terminates the gen_statem by calling + + Module:terminate/3 + + with Reason and + NewData, if specified. +

keep_state - The gen_statem will keep the current state, or - do a state transition to the current state if you like, - set NewData - and execute all Actions. - This is the same as - {next_state,CurrentState,NewData,Actions}. +

+ The gen_statem keeps the current state, or + does a state transition to the current state if you like, + sets NewData, + and executes all Actions. + This is the same as + {next_state,CurrentState,NewData,Actions}. +

keep_state_and_data - The gen_statem will keep the current state or - do a state transition to the current state if you like, - keep the current server data, - and execute all Actions. - This is the same as - {next_state,CurrentState,CurrentData,Actions}. +

+ The gen_statem keeps the current state or + does a state transition to the current state if you like, + keeps the current server data, + and executes all Actions. + This is the same as + {next_state,CurrentState,CurrentData,Actions}. +

All these terms are tuples or atoms and this property - will hold in any future version of gen_statem, - just in case you need such a promise. + will hold in any future version of gen_statem.

- - - - - Create a linked gen_statem process - -

- Creates a gen_statem process according - to OTP design principles - (using - proc_lib - primitives) - that is linked to the calling process. - This is essential when the gen_statem shall be part of - a supervision tree so it gets linked to its supervisor. -

-

- The gen_statem process calls - Module:init/1 - to initialize the server. To ensure a synchronized start-up - procedure, start_link/3,4 does not return until - Module:init/1 - has returned. -

-

- ServerName specifies the - - server_name() - - to register for the gen_statem. - If the gen_statem is started with start_link/3 - no ServerName is provided and - the gen_statem is not registered. -

-

Module is the name of the callback module.

-

- Args is an arbitrary term which is passed as - the argument to - Module:init/1. -

-

- If the option {timeout,Time} is present in - Opts, the gen_statem - is allowed to spend Time milliseconds initializing - or it will be terminated and the start function will return - {error,timeout}. -

-

- If the option - {debug,Dbgs} - is present in Opts, debugging through - sys is activated. -

-

- If the option {spawn_opt,SpawnOpts} is present in - Opts, SpawnOpts will be passed - as option list to - spawn_opt/2 - which is used to spawn the gen_statem process. -

- -

- Using the spawn option monitor is currently not - allowed, but will cause this function to fail with reason - badarg. -

-
-

- If the gen_statem is successfully created - and initialized this function returns - - {ok,Pid}, - - where Pid is the pid() - of the gen_statem. - If there already exists a process with the specified - ServerName this function returns - {error,{already_started,Pid}}, - where Pid is the pid() of that process. -

-

- If Module:init/1 fails with Reason, - this function returns - {error,Reason}. - If Module:init/1 returns - - {stop,Reason} - - or - ignore, - the process is terminated and this function - returns - - {error,Reason} - - or - ignore, - respectively. -

-
-
- - - - - - Create a stand-alone gen_statem process - -

- Creates a stand-alone gen_statem process according to - OTP design principles (using - proc_lib - primitives). - Since it does not get linked to the calling process - this start function can not be used by a supervisor - to start a child. -

-

- See start_link/3,4 - for a description of arguments and return values. -

-
-
- - - - Synchronously stop a generic server - -

- The same as - - stop(ServerRef, normal, infinity). - -

-
-
- - - Synchronously stop a generic server - -

- Orders the gen_statem - - ServerRef - - to exit with the given Reason - and waits for it to terminate. - The gen_statem will call - - Module:terminate/3 - - before exiting. -

-

- This function returns ok if the server terminates - with the expected reason. Any other reason than normal, - shutdown, or {shutdown,Term} will cause an - error report to be issued through - - error_logger:format/2. - - The default Reason is normal. -

-

- Timeout is an integer greater than zero - which specifies how many milliseconds to wait for the server to - terminate, or the atom infinity to wait indefinitely. - The default value is infinity. - If the server has not terminated within the specified time, - a timeout exception is raised. -

-

- If the process does not exist, a noproc exception - is raised. -

-
-
- - - - Make a synchronous call to a gen_statem + + + Make a synchronous call to a gen_statem.

Makes a synchronous call to the gen_statem @@ -1093,7 +970,7 @@ handle_event(_, _, State, Data) -> by sending a request and waiting until its reply arrives. - The gen_statem will call the + The gen_statem calls the state function with event_type() {call,From} and event content @@ -1108,33 +985,33 @@ handle_event(_, _, State, Data) -> and that Reply becomes the return value of this function.

-

- Timeout is an integer greater than zero +

+ Timeout is an integer > 0, which specifies how many milliseconds to wait for a reply, or the atom infinity to wait indefinitely, - which is the default. If no reply is received within + which is the default. If no reply is received within the specified time, the function call fails.

To avoid getting a late reply in the caller's - inbox this function spawns a proxy process that - does the call. A late reply gets delivered to the - dead proxy process hence gets discarded. This is + inbox, this function spawns a proxy process that + does the call. A late reply gets delivered to the + dead proxy process, hence gets discarded. This is less efficient than using Timeout =:= infinity.

-

- The call may fail for example if the gen_statem dies +

+ The call can fail, for example, if the gen_statem dies before or during this function call.

- - Send an asynchronous event to a gen_statem + + Send an asynchronous event to a gen_statem.

Sends an asynchronous event to the gen_statem @@ -1144,7 +1021,7 @@ handle_event(_, _, State, Data) -> and returns ok immediately, ignoring if the destination node or gen_statem does not exist. - The gen_statem will call the + The gen_statem calls the state function with event_type() cast and event content @@ -1154,44 +1031,8 @@ handle_event(_, _, State, Data) -> - - - Reply to a caller - -

- This function can be used by a gen_statem - to explicitly send a reply to a process that waits in - call/2 - when the reply cannot be defined in - the return value of a - state function. -

-

- From must be the term from the - - {call,From} - - argument to the - state function. - From and Reply - can also be specified using a - - reply_action() - - and multiple replies with a list of them. -

- -

- A reply sent with this function will not be visible - in sys debug output. -

-
-
-
- - - - Enter the gen_statem receive loop + + Enter the gen_statem receive loop.

The same as @@ -1204,12 +1045,13 @@ handle_event(_, _, State, Data) ->

+ - - Enter the gen_statem receive loop + + Enter the gen_statem receive loop.

- If Server_or_Actions is a list() + If Server_or_Actions is a list(), the same as enter_loop/7 except that no @@ -1228,34 +1070,35 @@ handle_event(_, _, State, Data) ->

+ - - Enter the gen_statem receive loop + + Enter the gen_statem receive loop. -

- Makes an the calling process become a gen_statem. - Does not return, instead the calling process will enter - the gen_statem receive loop and become +

+ Makes the calling process become a gen_statem. + Does not return, instead the calling process enters + the gen_statem receive loop and becomes a gen_statem server. The process must have been started using one of the start functions in - proc_lib. + proc_lib. The user is responsible for any initialization of the process, including registering a name for it.

-

+

This function is useful when a more complex initialization - procedure is needed than - the gen_statem behaviour provides. + procedure is needed than + the gen_statem behavior provides.

-

- Module, Opts and +

+ Module, Opts, and Server have the same meanings as when calling - - gen_statem:start[_link]/3,4. - - However, the + + start[_link]/3,4 + . + However, the server_name() @@ -1263,265 +1106,260 @@ handle_event(_, _, State, Data) -> before this function is called.

CallbackMode, State, - Data and Actions + Data, and Actions have the same meanings as in the return value of - Module:init/1. - Also, the callback module Module + Module:init/1. + Also, the callback module Module does not need to export an init/1 function.

- Failure: If the calling process was not started by a - proc_lib + The function fails if the calling process was not started by a + proc_lib start function, or if it is not registered - according to + according to server_name().

-
- - - -
- CALLBACK FUNCTIONS -

- The following functions should be exported from a - gen_statem callback module. -

-
- - - Module:init(Args) -> Result - Initialize process and internal state - - Args = term() - Result = {CallbackMode,State,Data} -  | {CallbackMode,State,Data,Actions} -  | {stop,Reason} | ignore - - CallbackMode = - callback_mode() - - State = state() - - Data = data() - - - Actions = - [action()] | - action() - - Reason = term() - + + + Reply to a caller. -

- Whenever a gen_statem is started using - start_link/3,4 - or - start/3,4, - this function is called by the new process to initialize - the implementation state and server data. -

-

- Args is the Args argument provided to the start - function. -

-

- If the initialization is successful, the function should - return {CallbackMode,State,Data} or - {CallbackMode,State,Data,Actions}. - CallbackMode selects the - callback mode. - of the gen_statem. - State is the initial - state() - and Data the initial server - data(). + This function can be used by a gen_statem + to explicitly send a reply to a process that waits in + call/2 + when the reply cannot be defined in + the return value of a + state function.

- The Actions - are executed when entering the first - state just as for a + From must be the term from argument + + {call,From} + + to the state function. + From and Reply + can also be specified using a + + reply_action() + + and multiple replies with a list of them.

+ +

+ A reply sent with this function is not visible + in sys debug output. +

+
+
+
+ + + + + Create a standalone gen_statem process. +

- If something goes wrong during the initialization - the function should return {stop,Reason} - or ignore. See - start_link/3,4. + Creates a standalone gen_statem process according to + OTP design principles (using + proc_lib + primitives). + As it does not get linked to the calling process, + this start function cannot be used by a supervisor + to start a child.

- This function may use - throw/1 - to return Result. + For a description of arguments and return values, see + start_link/3,4.

- Module:StateName(EventType, EventContent, Data) -> - StateFunctionResult - - Module:handle_event(EventType, EventContent, - State, Data) -> HandleEventResult - - Handle an event - - - EventType = - event_type() - - EventContent = term() - - State = - state() - - - Data = NewData = - data() - - - StateFunctionResult = - - state_function_result() - - - - HandleEventResult = - - handle_event_result() - - - + + + Create a linked gen_statem process.

- Whenever a gen_statem receives an event from - call/2, - cast/2 or - as a normal process message one of these functions is called. If - callback mode - is state_functions then Module:StateName/3 is called, - and if it is handle_event_function - then Module:handle_event/4 is called. + Creates a gen_statem process according + to OTP design principles + (using + proc_lib + primitives) + that is linked to the calling process. + This is essential when the gen_statem must be part of + a supervision tree so it gets linked to its supervisor.

- If EventType is - {call,From} - the caller is waiting for a reply. The reply can be sent - from this or from any other - state function - by returning with {reply,From,Reply} in - Actions, in - Replies - or by calling - reply(From, Reply). + The gen_statem process calls + Module:init/1 + to initialize the server. To ensure a synchronized startup + procedure, start_link/3,4 does not return until + Module:init/1 + has returned.

- If this function returns with a next state that - does not match equal (=/=) to the current state - all postponed events will be retried in the next state. + ServerName specifies the + + server_name() + + to register for the gen_statem. + If the gen_statem is started with start_link/3, + no ServerName is provided and + the gen_statem is not registered.

-

- The only difference between StateFunctionResult and - HandleEventResult is that for StateFunctionResult - the next state has to be an atom but for HandleEventResult - there is no restriction on the next state. +

Module is the name of the callback module.

+

+ Args is an arbitrary term that is passed as + the argument to + Module:init/1.

+ + +

+ If option {timeout,Time} is present in + Opts, the gen_statem + is allowed to spend Time milliseconds initializing + or it terminates and the start function returns + + {error,timeout}. +

+
+ +

+ If option + {debug,Dbgs} + is present in Opts, debugging through + sys is activated. +

+
+ +

+ If option {spawn_opt,SpawnOpts} is present in + Opts, SpawnOpts is passed + as option list to + erlang:spawn_opt/2, + which is used to spawn the gen_statem process. +

+
+
+ +

+ Using spawn option monitor is not + allowed, it cause this function to fail with reason + badarg. +

+

- See action() - for options that can be set and actions that can be done - by gen_statem after returning from this function. + If the gen_statem is successfully created + and initialized, this function returns + + {ok,Pid} + , + where Pid is the pid() + of the gen_statem. + If a process with the specified ServerName + exists already, this function returns + + {error,{already_started,Pid}}, + where Pid is the pid() of that process.

- These functions may use - throw/1, - to return the result. + If Module:init/1 fails with Reason, + this function returns + {error,Reason}. + If Module:init/1 returns + + {stop,Reason} + + or + ignore, + the process is terminated and this function + returns + + {error,Reason} + + or + ignore, + respectively.

- Module:terminate(Reason, State, Data) -> Ignored - Clean up before termination - - Reason = normal | shutdown | {shutdown,term()} | term() - State = state() - Data = data() - Ignored = term() - + + Synchronously stop a generic server. -

- This function is called by a gen_statem - when it is about to terminate. It should be the opposite of - Module:init/1 - and do any necessary cleaning up. When it returns, - the gen_statem terminates with Reason. The return - value is ignored.

-

- Reason is a term denoting the stop reason and - State - is the internal state of the gen_statem. -

-

- Reason depends on why the gen_statem - is terminating. - If it is because another callback function has returned a - stop tuple {stop,Reason} in - Actions, - Reason will have the value specified in that tuple. - If it is due to a failure, Reason is the error reason. +

+ The same as + + stop(ServerRef, normal, infinity). +

-

- If the gen_statem is part of a supervision tree and is - ordered by its supervisor to terminate, this function will be - called with Reason = shutdown if the following - conditions apply:

- - - the gen_statem has been set - to trap exit signals, and - - - the shutdown strategy as defined in the supervisor's - child specification is an integer timeout value, not - brutal_kill. - - -

- Even if the gen_statem is not - part of a supervision tree, this function will be called - if it receives an 'EXIT' message from its parent. - Reason will be the same as - in the 'EXIT' message. + + + + + + Synchronously stop a generic server. + +

+ Orders the gen_statem + + ServerRef + + to exit with the specified Reason + and waits for it to terminate. + The gen_statem calls + + Module:terminate/3 + + before exiting.

-

- Otherwise, the gen_statem will be immediately terminated. +

+ This function returns ok if the server terminates + with the expected reason. Any other reason than normal, + shutdown, or {shutdown,Term} causes an + error report to be issued through + + error_logger:format/2 + . + The default Reason is normal.

-

- Note that for any other reason than normal, - shutdown, or {shutdown,Term} - the gen_statem is assumed to terminate due to an error - and an error report is issued using - - error_logger:format/2. - +

+ Timeout is an integer > 0, + which specifies how many milliseconds to wait for the server to + terminate, or the atom infinity to wait indefinitely. + Defaults to infinity. + If the server does not terminate within the specified time, + a timeout exception is raised.

- This function may use - throw/1 - to return Ignored, which is ignored anyway. + If the process does not exist, a noproc exception + is raised.

+
+ +
+ Callback Functions +

+ The following functions are to be exported from a + gen_statem callback module. +

+
+ Module:code_change(OldVsn, OldState, OldData, Extra) -> Result - Update the internal state during upgrade/downgrade + Update the internal state during upgrade/downgrade. OldVsn = Vsn | {down,Vsn}   Vsn = term() @@ -1544,24 +1382,23 @@ handle_event(_, _, State, Data) ->

- This function is called by a gen_statem when it should - update its internal state during a release upgrade/downgrade, - that is when the instruction {update,Module,Change,...} - where Change={advanced,Extra} is given in the + This function is called by a gen_statem when it is to + update its internal state during a release upgrade/downgrade, + that is, when the instruction {update,Module,Change,...}, + where Change={advanced,Extra}, is specified in the appup - file. See - + file. For more information, see + OTP Design Principles - - for more information. + .

-

- In the case of an upgrade, OldVsn is Vsn, and - in the case of a downgrade, OldVsn is - {down,Vsn}. Vsn is defined by the vsn - attribute(s) of the old version of the callback module - Module. If no such attribute is defined, the version - is the checksum of the BEAM file. +

+ For an upgrade, OldVsn is Vsn, and + for a downgrade, OldVsn is + {down,Vsn}. Vsn is defined by the vsn + attribute(s) of the old version of the callback module + Module. If no such attribute is defined, the version + is the checksum of the Beam file.

@@ -1569,14 +1406,14 @@ handle_event(_, _, State, Data) -> callback mode - during release upgrade/downgrade, the upgrade is no problem - since the new code surely knows what callback mode - it needs, but for a downgrade this function will have to - know from the Extra argument that comes from the - appup + during release upgrade/downgrade, the upgrade is no problem, + as the new code surely knows what callback mode + it needs. However, for a downgrade this function must + know from argument Extra that comes from the + sasl:appup file what callback mode the old code did use. - It may also be possible to figure this out - from the {down,Vsn} argument since Vsn + It can also be possible to figure this out + from argument {down,Vsn}, as Vsn in effect defines the old callback module version.

@@ -1584,32 +1421,102 @@ handle_event(_, _, State, Data) -> OldState and OldData is the internal state of the gen_statem.

-

- Extra is passed as-is from the {advanced,Extra} - part of the update instruction. +

+ Extra is passed "as is" from the {advanced,Extra} + part of the update instruction.

-

- If successful, the function shall return the updated - internal state in an +

+ If successful, the function must return the updated + internal state in an {NewCallbackMode,NewState,NewData} tuple.

If the function returns Reason, the ongoing - upgrade will fail and roll back to the old release.

+ upgrade fails and rolls back to the old release.

- This function may use - throw/1 + This function can use + erlang:throw/1 to return Result or Reason.

+ + Module:init(Args) -> Result + Initialize process and internal state. + + Args = term() + Result = {CallbackMode,State,Data} +  | {CallbackMode,State,Data,Actions} +  | {stop,Reason} | ignore + + CallbackMode = + callback_mode() + + State = state() + + Data = data() + + + Actions = + [action()] | + action() + + Reason = term() + + + +

+ Whenever a gen_statem is started using + start_link/3,4 + or + start/3,4, + this function is called by the new process to initialize + the implementation state and server data. +

+

+ Args is the Args argument provided to the start + function. +

+

+ If the initialization is successful, the function is to + return {CallbackMode,State,Data} or + {CallbackMode,State,Data,Actions}. + CallbackMode selects the + + callback mode + of the gen_statem. + State is the initial + state() + and Data the initial server + data(). +

+

+ The Actions + are executed when entering the first + state just as for a + state function. +

+

+ If the initialization fails, + the function is to return {stop,Reason} + or ignore; see + start_link/3,4. +

+

+ This function can use + erlang:throw/1 + to return Result. +

+
+
+ Module:format_status(Opt, [PDict,State,Data]) -> Status Optional function for providing a term describing the - current gen_statem status + current gen_statem status. Opt = normal | terminate PDict = [{Key, Value}] @@ -1627,58 +1534,59 @@ handle_event(_, _, State, Data) -> -

- This callback is optional, so a callback module need not - export it. The gen_statem module provides a default - implementation of this function that returns - {State,Data}. If this callback fails the default - function will return {State,Info} +

+ This callback is optional, so a callback module does not need + to export it. The gen_statem module provides a default + implementation of this function that returns + {State,Data}. If this callback fails, the default + function returns {State,Info}, where Info informs of the crash but no details, to hide possibly sensitive data.

-

This function is called by a gen_statem process when:

+

This function is called by a gen_statem process when + any of the following apply:

- + One of - + sys:get_status/1,2 - is invoked to get the gen_statem status. Opt is set - to the atom normal for this case. + is invoked to get the gen_statem status. Opt is set + to the atom normal for this case. - + The gen_statem terminates abnormally and logs an error. Opt is set to the atom terminate for this case. - +

- This function is useful for customising the form and + This function is useful for changing the form and appearance of the gen_statem status for these cases. A - callback module wishing to customise the + callback module wishing to change the sys:get_status/1,2 - return value as well as how + return value and how its status appears in termination error logs exports an - instance of format_status/2 that returns a term + instance of format_status/2, which returns a term describing the current status of the gen_statem.

-

- PDict is the current value of the gen_statem's - process dictionary. +

+ PDict is the current value of the process dictionary + of the gen_statem.

-

+

State is the internal state of the gen_statem.

-

+

Data is the internal server data of the gen_statem.

-

- The function should return Status, a term that - customises the details of the current state and status of +

+ The function is to return Status, a term that + changes the details of the current state and status of the gen_statem. There are no restrictions on the form Status can take, but for the @@ -1687,38 +1595,197 @@ handle_event(_, _, State, Data) -> case (when Opt is normal), the recommended form for the Status value is [{data, [{"State", - Term}]}] where Term provides relevant details of - the gen_statem state. Following this recommendation isn't - required, but doing so will make the callback module status + Term}]}], where Term provides relevant details of + the gen_statem state. Following this recommendation is not + required, but it makes the callback module status consistent with the rest of the sys:get_status/1,2 return value.

-

+

One use for this function is to return compact alternative state representations to avoid having large state terms - printed in logfiles. Another is to hide sensitive data from + printed in log files. Another use is to hide sensitive data from being written to the error log.

- This function may use - throw/1 + This function can use + erlang:throw/1 to return Status.

+ + Module:StateName(EventType, EventContent, Data) -> + StateFunctionResult + + Module:handle_event(EventType, EventContent, + State, Data) -> HandleEventResult + + Handle an event. + + + EventType = + event_type() + + EventContent = term() + + State = + state() + + + Data = NewData = + data() + + + StateFunctionResult = + + state_function_result() + + + + HandleEventResult = + + handle_event_result() + + + + +

+ Whenever a gen_statem receives an event from + call/2, + cast/2, or + as a normal process message, one of these functions is called. If + callback mode + is state_functions, Module:StateName/3 is called, + and if it is handle_event_function, + Module:handle_event/4 is called. +

+

+ If EventType is + {call,From}, + the caller waits for a reply. The reply can be sent + from this or from any other + state function + by returning with {reply,From,Reply} in + Actions, in + Replies, + or by calling + reply(From, Reply). +

+

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

+

+ The only difference between StateFunctionResult and + HandleEventResult is that for StateFunctionResult + the next state must be an atom, but for HandleEventResult + there is no restriction on the next state. +

+

+ For options that can be set and actions that can be done + by gen_statem after returning from this function, + see action(). +

+

+ These functions can use + erlang:throw/1, + to return the result. +

+
+
+ + + Module:terminate(Reason, State, Data) -> Ignored + Clean up before termination. + + Reason = normal | shutdown | {shutdown,term()} | term() + State = state() + Data = data() + Ignored = term() + + +

+ This function is called by a gen_statem + when it is about to terminate. It is to be the opposite of + Module:init/1 + and do any necessary cleaning up. When it returns, + the gen_statem terminates with Reason. The return + value is ignored.

+

+ Reason is a term denoting the stop reason and + State + is the internal state of the gen_statem. +

+

+ Reason depends on why the gen_statem + is terminating. + If it is because another callback function has returned, a + stop tuple {stop,Reason} in + Actions, + Reason has the value specified in that tuple. + If it is because of a failure, Reason is the error reason. +

+

+ If the gen_statem is part of a supervision tree and is + ordered by its supervisor to terminate, this function is + called with Reason = shutdown if both the following + conditions apply:

+ + +

+ The gen_statem has been set + to trap exit signals. +

+
+ +

+ The shutdown strategy as defined in the supervisor's + child specification is an integer time-out value, not + brutal_kill. +

+
+
+

+ Even if the gen_statem is not + part of a supervision tree, this function is called + if it receives an 'EXIT' message from its parent. + Reason is the same as + in the 'EXIT' message. +

+

+ Otherwise, the gen_statem is immediately terminated. +

+

+ Notice that for any other reason than normal, + shutdown, or {shutdown,Term}, + the gen_statem is assumed to terminate because of an error + and an error report is issued using + + error_logger:format/2. + +

+

+ This function can use + erlang:throw/1 + to return Ignored, which is ignored anyway. +

+
+
- SEE ALSO + See Also

gen_event(3), gen_fsm(3), gen_server(3), - supervisor(3), proc_lib(3), + supervisor(3), sys(3)

diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml index ca0fce55e2..8b0fbed7c0 100644 --- a/system/doc/design_principles/statem.xml +++ b/system/doc/design_principles/statem.xml @@ -5,7 +5,7 @@
2016 - Ericsson AB. All Rights Reserved. + Ericsson AB. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,7 @@ - gen_statem Behaviour + gen_statem Behavior @@ -33,63 +33,60 @@

This section is to be read with the gen_statem(3) - manual page in STDLIB, where all interface functions and callback + manual page in STDLIB, where all interface functions and callback functions are described in detail.

-

- This is a new behaviour in OTP-19.0. - It has been thoroughly reviewed, is stable enough - to be used by at least two heavy OTP applications, and is here to stay. - But depending on user feedback, we do not expect - but might find it necessary to make minor - not backwards compatible changes into OTP-20.0, - so its state can be designated as "not quite experimental"... -

+ +

+ This is a new behavior in Erlang/OTP 19.0. + It has been thoroughly reviewed, is stable enough + to be used by at least two heavy OTP applications, and is here to stay. + Depending on user feedback, we do not expect + but can find it necessary to make minor + not backward compatible changes into Erlang/OTP 20.0. +

+
- Event Driven State Machines + Event-Driven State Machines

Established Automata theory does not deal much with how a state transition is triggered, - but in general assumes that the output is a function + but assumes that the output is a function of the input (and the state) and that they are some kind of values.

- For an Event Driven State Machine the input is an event + For an Event-Driven State Machine, the input is an event that triggers a state transition and the output is actions executed during the state transition. It can analogously to the mathematical model of a - Finite State Machine be described as - a set of relations of the form: + Finite-State Machine be described as + a set of relations of the following form:

 State(S) x Event(E) -> Actions(A), State(S')
-

These relations are interpreted as meaning:

-

- If we are in state S and event E occurs, we +

These relations are interpreted as follows: + if we are in state S and event E occurs, we are to perform actions A and make a transition to - state S'. -

-

- Note that S' may be equal to S. + state S'. Notice that S' can be equal to S.

- Since A and S' depend only on - S and E the kind of state machine described - here is a Mealy Machine. - (See for example the corresponding Wikipedia article) + As A and S' depend only on + S and E, the kind of state machine described + here is a Mealy Machine + (see, for example, the corresponding Wikipedia article).

- Like most gen_ behaviours, gen_statem keeps - a server Data besides the state. This and the fact that + Like most gen_ behaviors, gen_statem keeps + a server Data besides the state. Because of this, and as there is no restriction on the number of states - (assuming enough virtual machine memory) - or on the number of distinct input events actually makes - a state machine implemented with this behaviour Turing complete. - But it feels mostly like an Event Driven Mealy Machine. + (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. + But it feels mostly like an Event-Driven Mealy Machine.

@@ -99,38 +96,40 @@ State(S) x Event(E) -> Actions(A), State(S') Callback Modes

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

-
+    
+      
+        

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

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

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

-
+      
+      
+        

+ 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'}
+
+

- Both these modes allow other return tuples - that you can find in the + Both these modes allow other return tuples; see - reference manual. - - These other return tuples can for example stop the machine, - execute state transition actions on the machine engine itself + 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, and send replies.

@@ -139,54 +138,55 @@ handle_event(EventType, EventContent, State, Data) ->

The two callback modes - gives different possibilities + give different possibilities and restrictions, but one goal remains: you want to handle all possible combinations of events and states.

- You can for example do this by focusing on one state at the time - and for every state ensure that all events are handled, - or the other way around focus on one event at the time - and ensure that it is handled in every state, - or mix these strategies. + This can be done, for example, by focusing on one state at the time + and for every state ensure that all events are handled. + Alternatively, you can focus on one event at the time + and ensure that it is handled in every state. + You can also use a mix of these strategies.

- With state_functions you are restricted to use - atom only states, and the gen_statem engine dispatches - on state name for you. This encourages the callback module + With state_functions, you are restricted to use + atom-only states, and the gen_statem engine dispatches + on state name for you. This encourages the callback module to gather the implementation of all event actions particular - to one state in the same place in the code + to one state in the same place in the code, hence to focus on one state at the time.

- This mode fits well when you have a regular state diagram - like the ones in this chapter that describes all events and actions + This mode fits well when you have a regular state diagram, + like the ones in this chapter, which describes all events and actions belonging to a state visually around that state, and each state has its unique name.

- With handle_event_function you are free to mix strategies - as you like because all events and states - are handled in the the same callback function. + With handle_event_function, you are free to mix strategies, + as all events and states are handled in the same callback function.

This mode works equally well when you want to focus on - one event at the time or when you want to focus on - one state at the time, but the handle_event/4 function + one event at the time or on + one state at the time, but function + + Module:handle_event/4 quickly grows too large to handle without introducing dispatching.

- The mode enables the use of non-atom states for example - complex states or even hiearchical states. + The mode enables the use of non-atom states, for example, + complex states or even hierarchical states. If, for example, a state diagram is largely alike - for the client and for the server side of a protocol; - then you can have a state {StateName,server} or - {StateName,client} and since you do the dispatching - yourself you make StateName decide where in the code + for the client side and the server side of a protocol, + you can have a state {StateName,server} or + {StateName,client}. Also, as you do the dispatching + yourself, you make StateName decide where in the code to handle most events in the state. - The second element of the tuple is then used to select - whether to handle special client side or server side events. + The second element of the tuple is then used to select + whether to handle special client-side or server-side events.

@@ -196,31 +196,28 @@ handle_event(EventType, EventContent, State, Data) ->
Example

- This is an example starting off as equivalent to the the example in the - gen_fsm behaviour - description. In later chapters additions and tweaks are made + This example starts off as equivalent to the example in section + gen_fsm Behavior. + In later sections, 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 + The end of this chapter provides the example again with all the added features.

- A door with a code lock can be viewed as a state machine. - Initially, the door is locked. Anytime someone presses a button, - this generates an event. + A door with a code lock can be seen as a state machine. + Initially, the door is locked. When someone presses a button, + an event is generated. Depending on what buttons have been pressed before, the sequence so far can be correct, incomplete, or wrong. -

-

- If it is correct, the door is unlocked for 10 seconds (10000 ms). - If it is incomplete, we wait for another button to be pressed. If - it is is wrong, we start all over, - waiting for a new button sequence. + If correct, the door is unlocked for 10 seconds (10,000 milliseconds). + If incomplete, we wait for another button to be pressed. If + wrong, we start all over, waiting for a new button sequence.

- Code lock state diagram + Code Lock State Diagram

- We can implement such a code lock state machine using + This code lock state machine can be implemented using gen_statem with the following callback module:

@@ -241,7 +238,6 @@ start_link(Code) -> button(Digit) -> gen_statem:cast(?NAME, {button,Digit}). - init(Code) -> do_lock(), Data = #{code => Code, remaining => Code}, @@ -286,7 +282,7 @@ code_change(_Vsn, State, Data, _Extra) ->
Starting gen_statem

- In the example in the previous section, the gen_statem is + In the example in the previous section, gen_statem is started by calling code_lock:start_link(Code):

gen_statem:start_link({local,?NAME}, ?MODULE, Code, []). ]]>

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

- The first argument, {local,?NAME}, specifies - the name. In this case, the gen_statem is locally - registered as code_lock through the macro ?NAME. -

+ The first argument, {local,?NAME}, specifies + the name. In this case, the gen_statem is locally + registered as code_lock through the macro ?NAME. +

- If the name is omitted, the gen_statem is not registered. - Instead its pid must be used. The name can also be given - as {global,Name}, in which case the gen_statem is - registered using - - global:register_name/2. - -

+ If the name is omitted, the gen_statem is not registered. + 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 + + in Kernel. +

- The second argument, ?MODULE, is the name of - the callback module, that is; the module where the callback + The second argument, ?MODULE, is the name of + the callback module, that is, the module where the callback functions are located, which is this module. -

+

- The interface functions (start_link/1 and button/1) - are located in the same module as the callback functions - (init/1, locked/3, and open/3). - It is normally good programming practice to have the client - side and the server side code contained in one module. -

+ The interface functions (start_link/1 and button/1) + are located in the same module as the callback functions + (init/1, locked/3, and open/3). + It is normally good programming practice to have the client-side + code and the server-side code contained in one module. +

- The third argument, Code, is a list of digits that - is the correct unlock code which is passsed - to the callback function init/1. + The third argument, Code, is a list of digits, which + is the correct unlock code that is passed + to callback function init/1.

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

If name registration succeeds, the new gen_statem process - calls the callback function code_lock:init(Code). + calls callback function code_lock:init(Code). This function is expected to return {CallbackMode,State,Data}, where @@ -360,14 +357,14 @@ start_link(Code) -> state_functions - through the macro ?CALLBACK_MODE that is; each state + through 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. + in this case locked; assuming that the door is locked to begin + with. Data is the internal server data of the gen_statem. Here the server data is a map - with the key code that stores - the correct button sequence and the key remaining + with key code that stores + the correct button sequence, and key remaining that stores the remaining correct button sequence (the same as the code to begin with).

@@ -377,24 +374,25 @@ init(Code) -> Data = #{code => Code, remaining => Code}, {?CALLBACK_MODE,locked,Data}. ]]>
-

+

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

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

@@ -402,7 +400,7 @@ init(Code) ->
- Events and Handling them + Handling Events

The function notifying the code lock about a button event is implemented using @@ -415,9 +413,9 @@ button(Digit) -> ]]>

The first argument is the name of the gen_statem and must - agree with the name used to start it so therefore we use the + agree with the name used to start it. So, we use the same macro ?NAME as when starting. - {button,Digit} is the actual event content. + {button,Digit} is the event content.

The event is made into a message and sent to the gen_statem. @@ -452,19 +450,19 @@ open(cast, {button,_}, Data) -> ]]>

If the door is locked and a button is pressed, the pressed - button is compared with the next correct button and, - depending on the result, the door is either unlocked + button is compared with the next correct button. + Depending on the result, the door is either unlocked and the gen_statem goes to state open, or the door remains in state locked.

- If the pressed button is incorrect the server data + If the pressed button is incorrect, the server data restarts from the start of the code sequence.

- In state open any button locks the door since - any event cancels the event timer so we will not get - a timeout event after a button event. + In state open, any button locks the door, as + any event cancels the event timer, so no + time-out event occurs after a button event.

@@ -478,11 +476,11 @@ open(cast, {button,_}, Data) -> {next_state,open,Data#{remaining := Code},10000}; ]]>

- 10000 is a time-out value in milliseconds. - After this time, that is; 10 seconds, a time-out occurs. + 10,000 is a time-out value in milliseconds. + After this time (10 seconds), a time-out occurs. Then, StateName(timeout, 10000, Data) is called. The time-out occurs when the door has been in state open - for 10 seconds. After that the door is locked again: + for 10 seconds. After that the door is locked again:

@@ -496,16 +494,16 @@ open(timeout, _, Data) ->
All State Events

- Sometimes an event can arrive in any state of the gen_statem. + Sometimes events can arrive in any state of the gen_statem. It is convenient to handle these in a common state handler function that all state functions call for events not specific to the state.

- Let's introduce a code_length/0 function that returns + Consider a code_length/0 function that returns the length of the correct code - (that should not be sensitive to reveal...). - We'll dispatch all events that are not state specific - to the common function handle_event/3. + (that should not be sensitive to reveal). + We dispatch all events that are not state-specific + to the common function handle_event/3:

This example uses 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,9 +543,12 @@ handle_event({call,From}, code_length, #{code := Code} = Data) ->
One Event Handler

- If you use the mode handle_event_function - all events are handled in handle_event/4 and we - may (but do not have to) use an event-centered approach + If mode handle_event_function is used, + all events are handled in + + 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:

The gen_statem is automatically terminated by its supervisor. Exactly how this is done is defined by a shutdown strategy - set in the supervisor. + set in the supervisor.

If it is necessary to clean up before termination, the shutdown - strategy must be a time-out value and the gen_statem must - in the init/1 function set itself to trap exit signals + 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 shutdown, the gen_statem then calls - the callback function - terminate(shutdown, State, Data): + process_flag(trap_exit, true) + . + When ordered to shut down, the gen_statem then calls + callback function terminate(shutdown, State, Data):

@@ -617,9 +617,9 @@ init(Args) -> ... ]]>

- In this example we let the terminate/3 function - lock the door if it is open so we do not accidentally leave the door - open when the supervision tree terminates. + In the following example, function terminate/3 + locks the door if it is open, so we do not accidentally leave the door + open when the supervision tree terminates:

@@ -634,8 +634,8 @@ 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:

gen_statem:stop(?NAME). ]]>

- This makes the gen_statem call the terminate/3 - callback function just like for a supervised server + This makes the gen_statem call callback function + terminate/3 just like for a supervised server and waits for the process to terminate.

@@ -659,15 +659,15 @@ stop() ->
Actions

- In the first chapters we mentioned actions as a part of - the general state machine model, and these actions - are implemented with the code the gen_statem - callback module executes in an event handling + In the first sections actions were mentioned as a part of + the general state machine model. These actions + are implemented with the code that callback module + gen_statem executes in an event-handling callback function before returning to the gen_statem engine.

- There are more specific state transition actions + There are more specific state-transition actions 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 @@ -680,27 +680,28 @@ stop() -> from the - callback function. - + callback function + . These state transition actions affect the gen_statem - engine itself. They can: + engine itself and can do the following:

- Postpone the current event. - Hibernate the gen_statem. - Start an event timeout. - Reply to a caller. - Generate the next event to handle. + Postpone the current event + Hibernate the gen_statem + Start an event time-out + Reply to a caller + Generate the next event to handle

- We have mentioned the event timeout - and replying to a caller in the example above. - An example of event postponing comes in later in this chapter. - See the + In the example earlier was mentioned the event time-out + and replying to a caller. + An example of event postponing is included later in this chapter. + For details, see the - reference manual + gen_statem(3) - for details. You can for example actually reply to several callers + manual page. + You can, for example, reply to many callers and generate multiple next events to handle.

@@ -710,16 +711,16 @@ stop() ->
Event Types

- So far we have mentioned a few + 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.

- Here is the complete list of event types and where + The following is a complete list of event types and where they come from:

@@ -735,13 +736,13 @@ stop() -> Generated by 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 @@ -750,15 +751,15 @@ stop() -> timeout - Generated by the state transition action + Generated by state transition action {timeout,Time,EventContent} (or its short form Time) timer timing out. internal - Generated by the state transition action + Generated by state transition action {next_event,internal,EventContent}. - In fact all event types above can be generated using + All event types above can be generated using {next_event,EventType,EventContent}. @@ -767,34 +768,34 @@ stop() ->
- State Timeouts + State Time-Outs

- The timeout event generated by the state transition action - {timeout,Time,EventContent} is an event timeout, - that is; if an event arrives the timer is cancelled. - You get either an event or a timeout but not both. + The time-out event generated by state transition action + {timeout,Time,EventContent} is an event time-out, + that is, if an event arrives the timer is cancelled. + You get either an event or a time-out, but not both.

- Often you want a timer to not be cancelled by any event + Often you want a timer not to be cancelled by any event or you want to start a timer in one state and respond - to the timeout in another. This can be accomplished - with a regular erlang timer: + to the time-out in another. This can be accomplished + with a regular Erlang timer: erlang:start_timer.

- Looking at the example in this chapter so far; using the + For the example so far in this chapter: using the gen_statem event timer has the consequence that if a button event is generated while in the open state, - the timeout is cancelled and the button event is delivered. - Therefore we chose to lock the door if this happended. + the time-out is cancelled and the button event is delivered. + So, we choose to lock the door if this occurred.

- Suppose we do not want a button to lock the door, + Suppose that we do not want a button to lock the door, instead we want to ignore button events in the open state. Then we start a timer when entering the open state - and wait for it to expire while ignoring button events: + and waits for it to expire while ignoring button events:

... ]]>

- If you need to cancel a timer due to some other event you can use + If you need to cancel a timer because of some other event, you can use - erlang:cancel_timer(Tref). - - Note that a timeout message can not arrive after this, + 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 make sure you do not accidentally postpone such messages. + so ensure that you do not accidentally postpone such messages.

- Another way to cancel a timer is to not cancel it, - but instead to ignore it if it arrives in a state + Another way to cancel a timer is not to cancel it, + but to ignore it if it arrives in a state where it is known to be late.

@@ -839,7 +840,7 @@ open(cast, {button,_}, Data) -> If you want to ignore a particular event in the current state and handle it in a future state, you can postpone the event. A postponed event is retried after the state has - changed i.e OldState =/= NewState. + changed, that is, OldState =/= NewState.

Postponing is ordered by the state transition @@ -850,8 +851,8 @@ open(cast, {button,_}, Data) ->

In this example, instead of ignoring button events - while in the open state we can postpone them - and they will be queued and later handled in the locked state: + while in the open state, we can postpone them + and they are queued and later handled in the locked state:

... ]]>

- 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; - should a change in the item value affect which events that - are handled, then this item ought to be part of the state. + 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: + if a change in the item value affects which events that + are handled, then this item is to be part of the state.

- What you want to avoid is that you maybe much later decide - to postpone an event in one state and by misfortune it is never retried - because the code only changes the Data but not the State. + You want to avoid that you maybe much later decide + to postpone an event in one state and by misfortune it is never retried, + as the code only changes the Data but not the State.

@@ -883,7 +884,7 @@ open(cast, {button,_}, Data) -> or from the context.

- Possible actions may be; ignore as in drop the event + Possible actions: ignore as in drop the event (maybe log it) or deal with the event in some other state as in postpone it.

@@ -892,10 +893,10 @@ open(cast, {button,_}, Data) ->
Selective Receive

- Erlang's selective receive statement is often used to - describe simple state machine examples in straightforward - Erlang code. Here is a possible implementation of - the first example: + Erlang's selective receive statement is often used to + describe simple state machine examples in straightforward + Erlang code. The following is a possible implementation of + the first example:

io:format("Open~n", []). ]]>

- The selective receive in this case causes open - to implicitly postpone any events to the locked state. + The selective receive in this case causes implicitly open + to postpone any events to the locked state.

- A selective receive can not be used from a gen_statem - behaviour just as for any gen_* behavior - since the receive statement is within the gen_* engine itself. - It has to be there because all + A selective receive cannot be used from a gen_statem + behavior as for any gen_* behavior, + as the receive statement is within the gen_* engine itself. + It must be there because all sys - compatible behaviours must respond to system messages and therefore + compatible behaviors must respond to system messages and therefore do that in their engine receive loop, passing non-system messages to the callback module.

@@ -955,15 +956,15 @@ do_unlock() -> action - postpone is designed to be able to model - selective receives. A selective receive implicitly postpones + postpone is designed to model + selective receives. A selective receive implicitly postpones any not received events, but the postpone state transition action explicitly postpones one received event.

- Other than that both mechanisms have got the same theoretical + Both mechanisms have the same theoretical time and memory complexity, while the selective receive - language construct has got smaller constant factors. + language construct has smaller constant factors.

@@ -971,9 +972,9 @@ do_unlock() ->
- Self Generated Events + Self-Generated Events

- It may be beneficial in some cases to be able to generate events + It can sometimes be beneficial to be able to generate events to your own state machine. This can be done with the state transition @@ -984,30 +985,30 @@ do_unlock() ->

You can generate events of any existing - type, - - but the internal type can only be generated through the - next_event action and hence can not come from an external source, + 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 from your state machine to itself.

- One example of using self generated events may be when you have + One example of using self-generated events can be when you have a state machine specification that uses state entry actions. - That you could code using a dedicated function - to do the state transition. But if you want that code to be - visible besides the other state logic you can insert + You can code that using a dedicated function + to do the state transition. But if you want that code to be + visible besides the other state logic, you can insert an internal event that does the entry actions. This has the same unfortunate consequence as using - state transition functions that everywhere you go to - the state in question you will have to explicitly + state transition functions: everywhere you go to + the state, you must explicitly insert the internal event - or use state transition function. + or use a state transition function.

- Here is an implementation of entry actions + The following is an implementation of entry actions using internal events with content enter - utilizing a helper function enter/3 for state entry: + using a helper function enter/3 for state entry:

Example Revisited

- Here is the example after all mentioned modifications - and some more utilizing the entry actions, + This section includes the example after all mentioned modifications + and some more using the entry actions, which deserves a new state diagram:

- Code lock state diagram revisited + Code Lock State Diagram Revisited

- Note that this state diagram does not specify how to handle - a button event in the state open, so you will have to - read some other place that is here that unspecified events - shall be ignored as in not consumed but handled in some other state. - Nor does it show that the code_length/0 call shall be - handled in every state. + Notice that this state diagram does not specify how to handle + a button event in the state open. So, you need to + read somewhere else that unspecified events + must be ignored as in not consumed but handled in some other state. + Also, the state diagram does not show that the code_length/0 + call must be handled in every state.

@@ -1147,10 +1148,11 @@ code_change(_Vsn, State, Data, _Extra) ->
Callback Mode: handle_event_function

- What to change to use one handle_event/4 function. - Here a clean first-dispatch-on-event approach - does not work that well due to the generated - entry actions: + 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:

]]>

- Note that postponing buttons from the locked state + Notice that postponing buttons from the locked state to the open state feels like the wrong thing to do for a code lock, but it at least illustrates event postponing.

@@ -1206,33 +1208,33 @@ handle_event({call,From}, code_length, _State, #{code := Code}) ->
Filter the State

- The example servers so far in this chapter will for example - when killed by an exit signal or due to an internal error - print out the full internal state in the error log. + The example servers so far in this chapter + print the full internal state in the error log, for example, + when killed by an exit signal or because of an internal error. This state contains both the code lock code - and which digits that remains to unlock. + and which digits that remain to unlock.

This state data can be regarded as sensitive, and maybe not what you want in the error log - because of something unpredictable happening. + because of some unpredictable event.

Another reason to filter the state can be - that the state is too big to print out since it fills + that the state is too large to print, as it fills the error log with uninteresting details.

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

Module:format_status/2 - function. If you do not a default implementation is used that + 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}. + the Data term, that is, StateData = {State,Data}.

@@ -1275,54 +1277,56 @@ format_status(Opt, [_PDict,State,Data]) -> handle_event_function - enables using a non-atom state as described in + enables using a non-atom state as described in section - Callback Modes, - - for example a complex state term like a tuple. + Callback Modes + , + for example, a complex state term like a tuple.

One reason to use this is when you have - a state item that affects the event handling - in particular when combining that with postponing events. - Let us complicate the previous example + a state item that affects the event handling, + in particular in combination with postponing events. + We complicate the previous example by introducing a configurable lock button - (this is the state item in question) - that in the open state immediately locks the door, + (this is the state item in question), + which in the open state immediately locks the door, and an API function set_lock_button/1 to set the lock button.

Suppose now that we call set_lock_button while the door is open, and have already postponed a button event - that up until now was not the lock button; - the sensible thing might be to say that - the button was pressed too early so it should - not be recognized as the lock button, - but then it might be surprising that a button event + that until now was not the lock button. + The sensible thing can be to say that + the button was pressed too early so it is + not to be recognized as the lock button. + However, then it can be surprising that a button event that now is the lock button event arrives (as retried postponed) immediately after the state transits to locked.

- So let us make the button/1 function synchronous - by using gen_statem:call, + So we make the button/1 function synchronous + by using + + gen_statem:call and still postpone its events in the open state. Then a call to button/1 during the open - state will not return until the state transits to locked - since it is there the event is handled and the reply is sent. + state does not return until the state transits to locked, + as it is there the event is handled and the reply is sent.

- If now one process calls set_lock_button/1 - to change the lock button while some other process - hangs in button/1 with the new lock button - it could be expected that the hanging lock button call + If a process now calls set_lock_button/1 + to change the lock button while another process + hangs in button/1 with the new lock button, + it can be expected that the hanging lock button call immediately takes effect and locks the lock. - Therefore we make the current lock button a part of the state - so when we change the lock button the state will change - and all postponed events will be retried. + Therefore, we make the current lock button a part of the state, + so that when we change the lock button, the state changes + and all postponed events are retried.

- We define the state as {StateName,LockButton} + We define the state as {StateName,LockButton}, where StateName is as before and LockButton is the current lock button:

@@ -1441,10 +1445,9 @@ format_status(Opt, [_PDict,State,Data]) -> end. ]]>

- It may be an ill-fitting model for a physical code lock - that the button/1 call might hang until the lock - is locked. But for an API in general it is really not - that strange. + It can be an ill-fitting model for a physical code lock + that the button/1 call can hang until the lock + is locked. But for an API in general it is not that strange.

@@ -1457,26 +1460,25 @@ format_status(Opt, [_PDict,State,Data]) -> and they have some state(s) in their lifetime in which the servers can be expected to idle for a while, and the amount of heap memory all these servers need - is a problem; then it is possible to minimize - the memory footprint of a server by hibernating it through + is a problem, then the memory footprint of a server + can be mimimized by hibernating it through proc_lib:hibernate/3.

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

- We can in this example hibernate in the {open,_} state - since what normally happens in that state is that - the state timeout after a while + We can in this example hibernate in the {open,_} state, + because what normally occurs in that state is that + the state time-out after a while triggers a transition to {locked,_}:

action list on the last line when entering the {open,_} state is the only change. - If any event arrives in the {open,_}, state we - do not bother to re-hibernate, so the server stays + If any event arrives in the {open,_}, state, we + do not bother to rehibernate, so the server stays awake after any event.

To change that we would need to insert - the hibernate action in more places, - for example for the state independent set_lock_button + action hibernate in more places. + For example, for the state-independent set_lock_button and code_length operations that then would have to be aware of using hibernate while in the - {open,_} state which would clutter the code. + {open,_} state, which would clutter the code.

- This server probably does not use an amount of + This server probably does not use heap memory worth hibernating for. - To gain anything from hibernation your server would - have to actually produce some garbage during callback execution, - for which this example server may serve as a bad example. + To gain anything from hibernation, your server would + have to produce some garbage during callback execution, + for which this example server can serve as a bad example.

-- cgit v1.2.3 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 --- lib/stdlib/doc/src/gen_statem.xml | 296 ++++++++++---------------------- system/doc/design_principles/statem.xml | 184 ++++++-------------- 2 files changed, 145 insertions(+), 335 deletions(-) diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index be90488a1f..b1d9799917 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -84,9 +84,7 @@ using this module has a standard set of interface functions and includes functionality for tracing and error reporting. It also fits into an OTP supervision tree. For more information, see - - OTP Design Principles - . + OTP Design Principles.

A gen_statem assumes all specific parts to be located in a @@ -136,28 +134,23 @@ erlang:'!' -----> Module:StateName/3 callback mode is state_functions, the state must be an atom and is used as the state function name; see - - Module:StateName/3 - . + Module:StateName/3. This gathers all code for a specific state in one function and hence dispatches on state first. Notice that in this mode the mandatory callback function - - Module:terminate/3 - makes the state name terminate unusable. + Module:terminate/3 + makes the state name terminate unusable.

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

@@ -178,15 +171,12 @@ erlang:'!' -----> Module:StateName/3

The state function can insert events using the - - action() next_event - + action() + next_event and such an event is inserted as the next to present to the state function. That is, as if it is the oldest incoming event. A dedicated - - event_type() - + event_type() internal can be used for such events making them impossible to mistake for external events.

@@ -206,21 +196,18 @@ erlang:'!' -----> Module:StateName/3

For the details of a state transition, see type - - transition_option() - . + transition_option().

A gen_statem handles system messages as described in sys. - The sysmodule can be used for debugging a gen_statem. + The sys module can be used for debugging a gen_statem.

Notice that a gen_statem does not trap exit signals automatically, this must be explicitly initiated in the callback module (by calling - - process_flag(trap_exit, true). + process_flag(trap_exit, true).

Unless otherwise stated, all functions in this module fail if @@ -229,9 +216,7 @@ erlang:'!' -----> Module:StateName/3

The gen_statem process can go into hibernation; see - - proc_lib:hibernate/3 - . + proc_lib:hibernate/3. It is done when a state function or Module:init/1 @@ -242,9 +227,7 @@ erlang:'!' -----> Module:StateName/3 However, use this feature with care, as hibernation can be too costly to use after every event; see - - erlang:hibernate/3 - . + erlang:hibernate/3.

@@ -257,7 +240,7 @@ erlang:'!' -----> Module:StateName/3 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. + pushed to switch on.

The following is the complete callback module file pushbutton.erl:

@@ -381,13 +364,10 @@ handle_event(_, _, State, Data) ->

Name specification to use when starting a gen_statem server. See - - start_link/3 - + start_link/3 and - - server_ref() - below. + server_ref() + below.

@@ -398,14 +378,12 @@ handle_event(_, _, State, Data) -> Server specification to use when addressing a gen_statem server. See call/2 and - - server_name() - + server_name() above.

It can be:

- pid() | LocalName + pid() | LocalName

The gen_statem is locally registered. @@ -421,21 +399,21 @@ handle_event(_, _, State, Data) -> {global,GlobalName}

- The gen_statem is globally registered - in kernel:global. + The gen_statem is globally registered in + kernel:global.

{via,RegMod,ViaName}

- The gen_statem is registered through + The gen_statem is registered in an alternative process registry. The registry callback module RegMod is to export functions register_name/2, unregister_name/1, whereis_name/1, and send/2, - which are to behave like the corresponding functions - in kernel:global. + which are to behave like the corresponding functions in + kernel:global. Thus, {via,global,GlobalName} is the same as {global,GlobalName}.

@@ -482,9 +460,7 @@ handle_event(_, _, State, Data) ->

Destination to use when replying through, for example, the - - action() - + action() {reply,From,Reply} to a process that has called the gen_statem server using call/2. @@ -554,9 +530,7 @@ handle_event(_, _, State, Data) -> or when calling enter_loop/5,6,7, and with the return value from - - Module:code_change/4 - . + Module:code_change/4.

state_functions @@ -565,9 +539,7 @@ handle_event(_, _, State, Data) -> The state must be of type state_name() and one callback function per state, that is, - - Module:StateName/3 - , + Module:StateName/3, is used.

@@ -575,9 +547,7 @@ handle_event(_, _, State, Data) ->

The state can be any term and the callback function - - Module:handle_event/4 - + Module:handle_event/4 is used for all states.

@@ -604,9 +574,7 @@ handle_event(_, _, State, Data) ->

If - - postpone() - + postpone() is true, the current event is postponed.

@@ -620,9 +588,7 @@ handle_event(_, _, State, Data) ->

All events stored with - - action() - + action() next_event are inserted in the queue to be processed before all other events. @@ -631,13 +597,9 @@ handle_event(_, _, State, Data) ->

If an - - event_timeout() - + event_timeout() is set through - - action() - + action() timeout, an event timer can be started or a time-out zero event can be enqueued. @@ -651,9 +613,7 @@ handle_event(_, _, State, Data) -> otherwise the gen_statem goes into receive or hibernation (if - - hibernate() - + hibernate() is true) to wait for the next message. In hibernation the next non-system event awakens the gen_statem, or rather @@ -681,16 +641,13 @@ handle_event(_, _, State, Data) ->

If true, hibernates the gen_statem by calling - - proc_lib:hibernate/3 - + proc_lib:hibernate/3 before going into receive to wait for a new external event. If there are enqueued events, to prevent receiving any new event, an - - erlang:garbage_collect/0 - is done instead to simulate + erlang:garbage_collect/0 + is done instead to simulate that the gen_statem entered hibernation and immediately got awakened by the oldest enqueued event.

@@ -731,8 +688,8 @@ handle_event(_, _, State, Data) ->

These state transition actions can be invoked by returning them from the - state function, - from Module:init/1 + state function, from + Module:init/1 or by giving them to enter_loop/6,7.

@@ -741,15 +698,11 @@ handle_event(_, _, State, Data) ->

Actions that set - - transition options - + transition options override any previous of the same type, so the last in the containing list wins. For example, the last - - event_timeout() - + event_timeout() overrides any other event_timeout() in the list.

@@ -757,12 +710,8 @@ handle_event(_, _, State, Data) ->

Sets the - - transition_option() - - - postpone() - + transition_option() + postpone() for this state transition. This action is ignored when returned from Module:init/1 @@ -775,12 +724,8 @@ handle_event(_, _, State, Data) ->

Sets the - - transition_option() - - - hibernate() - + transition_option() + hibernate() for this state transition.

@@ -792,22 +737,16 @@ handle_event(_, _, State, Data) -> This form exists to make the state function return value {next_state,NextState,NewData,Timeout} - allowed like for - - gen_fsm:Module:StateName/2 - . + allowed like for gen_fsm's + Module:StateName/2.

timeout

Sets the - - transition_option() - - - event_timeout() - + transition_option() + event_timeout() to Time with EventContent.

@@ -832,9 +771,7 @@ handle_event(_, _, State, Data) ->

An event of type - - internal - + internal is to be used when you want to reliably distinguish an event inserted this way from any external event.

@@ -849,9 +786,7 @@ handle_event(_, _, State, Data) -> Replies to a caller waiting for a reply in call/2. From must be the term from argument - - {call,From} - + {call,From} to the state function.

@@ -907,9 +842,7 @@ handle_event(_, _, State, Data) ->

Terminates the gen_statem by calling - - Module:terminate/3 - + Module:terminate/3 with Reason and NewData, if specified.

@@ -919,9 +852,7 @@ handle_event(_, _, State, Data) ->

Sends all Replies, then terminates the gen_statem by calling - - Module:terminate/3 - + Module:terminate/3 with Reason and NewData, if specified.

@@ -965,9 +896,7 @@ handle_event(_, _, State, Data) ->

Makes a synchronous call to the gen_statem - - ServerRef - + ServerRef by sending a request and waiting until its reply arrives. The gen_statem calls the @@ -1015,9 +944,7 @@ handle_event(_, _, State, Data) ->

Sends an asynchronous event to the gen_statem - - ServerRef - + ServerRef and returns ok immediately, ignoring if the destination node or gen_statem does not exist. @@ -1038,9 +965,7 @@ handle_event(_, _, State, Data) -> The same as enter_loop/7 except that no - - server_name() - + server_name() must have been registered.

@@ -1055,9 +980,7 @@ handle_event(_, _, State, Data) -> the same as enter_loop/7 except that no - - server_name() - + server_name() must have been registered and Actions = Server_or_Actions.

@@ -1095,13 +1018,9 @@ handle_event(_, _, State, Data) -> Module, Opts, and Server have the same meanings as when calling - - start[_link]/3,4 - . + start[_link]/3,4. However, the - - server_name() - + server_name() name must have been registered accordingly before this function is called.

@@ -1137,16 +1056,12 @@ handle_event(_, _, State, Data) ->

From must be the term from argument - - {call,From} - + {call,From} to the state function. From and Reply can also be specified using a - - reply_action() - + reply_action() and multiple replies with a list of them.

@@ -1204,9 +1119,7 @@ handle_event(_, _, State, Data) ->

ServerName specifies the - - server_name() - + server_name() to register for the gen_statem. If the gen_statem is started with start_link/3, no ServerName is provided and @@ -1225,8 +1138,7 @@ handle_event(_, _, State, Data) -> Opts, the gen_statem is allowed to spend Time milliseconds initializing or it terminates and the start function returns - - {error,timeout}. + {error,timeout}.

@@ -1241,8 +1153,8 @@ handle_event(_, _, State, Data) ->

If option {spawn_opt,SpawnOpts} is present in Opts, SpawnOpts is passed - as option list to - erlang:spawn_opt/2, + as option list to + erlang:spawn_opt/2, which is used to spawn the gen_statem process.

@@ -1250,22 +1162,19 @@ handle_event(_, _, State, Data) ->

Using spawn option monitor is not - allowed, it cause this function to fail with reason + allowed, it causes this function to fail with reason badarg.

If the gen_statem is successfully created and initialized, this function returns - - {ok,Pid} - , + {ok,Pid}, where Pid is the pid() of the gen_statem. If a process with the specified ServerName exists already, this function returns - - {error,{already_started,Pid}}, + {error,{already_started,Pid}}, where Pid is the pid() of that process.

@@ -1273,16 +1182,12 @@ handle_event(_, _, State, Data) -> this function returns {error,Reason}. If Module:init/1 returns - - {stop,Reason} - + {stop,Reason} or ignore, the process is terminated and this function returns - - {error,Reason} - + {error,Reason} or ignore, respectively. @@ -1296,9 +1201,7 @@ handle_event(_, _, State, Data) ->

The same as - - stop(ServerRef, normal, infinity). - + stop(ServerRef, normal, infinity).

@@ -1309,15 +1212,11 @@ handle_event(_, _, State, Data) ->

Orders the gen_statem - - ServerRef - + ServerRef to exit with the specified Reason and waits for it to terminate. The gen_statem calls - - Module:terminate/3 - + Module:terminate/3 before exiting.

@@ -1325,9 +1224,7 @@ handle_event(_, _, State, Data) -> with the expected reason. Any other reason than normal, shutdown, or {shutdown,Term} causes an error report to be issued through - - error_logger:format/2 - . + error_logger:format/2. The default Reason is normal.

@@ -1388,9 +1285,7 @@ handle_event(_, _, State, Data) -> where Change={advanced,Extra}, is specified in the appup file. For more information, see - - OTP Design Principles - . + OTP Design Principles.

For an upgrade, OldVsn is Vsn, and @@ -1403,9 +1298,7 @@ handle_event(_, _, State, Data) ->

If you would dare to change - - callback mode - + callback mode during release upgrade/downgrade, the upgrade is no problem, as the new code surely knows what callback mode it needs. However, for a downgrade this function must @@ -1483,8 +1376,7 @@ handle_event(_, _, State, Data) -> return {CallbackMode,State,Data} or {CallbackMode,State,Data,Actions}. CallbackMode selects the - - callback mode + callback mode of the gen_statem. State is the initial state() @@ -1549,9 +1441,7 @@ handle_event(_, _, State, Data) -> One of - - sys:get_status/1,2 - + sys:get_status/1,2 is invoked to get the gen_statem status. Opt is set to the atom normal for this case. @@ -1564,9 +1454,7 @@ handle_event(_, _, State, Data) -> This function is useful for changing the form and appearance of the gen_statem status for these cases. A callback module wishing to change the - - sys:get_status/1,2 - + sys:get_status/1,2 return value and how its status appears in termination error logs exports an instance of format_status/2, which returns a term @@ -1589,9 +1477,7 @@ handle_event(_, _, State, Data) -> changes the details of the current state and status of the gen_statem. There are no restrictions on the form Status can take, but for the - - sys:get_status/1,2 - + sys:get_status/1,2 case (when Opt is normal), the recommended form for the Status value is [{data, [{"State", @@ -1599,9 +1485,7 @@ handle_event(_, _, State, Data) -> the gen_statem state. Following this recommendation is not required, but it makes the callback module status consistent with the rest of the - - sys:get_status/1,2 - + sys:get_status/1,2 return value.

@@ -1642,15 +1526,11 @@ handle_event(_, _, State, Data) -> StateFunctionResult = - - state_function_result() - + state_function_result() HandleEventResult = - - handle_event_result() - + handle_event_result() @@ -1766,9 +1646,7 @@ handle_event(_, _, State, Data) -> shutdown, or {shutdown,Term}, the gen_statem is assumed to terminate because of an error and an error report is issued using - - error_logger:format/2. - + error_logger:format/2.

This function can use @@ -1781,11 +1659,13 @@ handle_event(_, _, State, Data) ->

See Also -

gen_event(3), +

+ gen_event(3), gen_fsm(3), gen_server(3), proc_lib(3), supervisor(3), - sys(3)

+ sys(3). +

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