diff options
Diffstat (limited to 'system/doc/design_principles/fsm.xml')
-rw-r--r-- | system/doc/design_principles/fsm.xml | 168 |
1 files changed, 88 insertions, 80 deletions
diff --git a/system/doc/design_principles/fsm.xml b/system/doc/design_principles/fsm.xml index 9dce159dca..ef961f5fad 100644 --- a/system/doc/design_principles/fsm.xml +++ b/system/doc/design_principles/fsm.xml @@ -21,32 +21,33 @@ </legalnotice> - <title>Gen_Fsm Behaviour</title> + <title>gen_fsm Behaviour</title> <prepared></prepared> <docno></docno> <date></date> <rev></rev> <file>fsm.xml</file> </header> - <p>This chapter should be read in conjunction with <c>gen_fsm(3)</c>, - where all interface functions and callback functions are described - in detail.</p> + <marker id="gen_fsm behaviour"></marker> + <p>This section is to be read with the <c>gen_fsm(3)</c> manual page + in STDLIB, where all interface functions and callback + functions are described in detail.</p> <section> - <title>Finite State Machines</title> - <p>A finite state machine, FSM, can be described as a set of + <title>Finite-State Machines</title> + <p>A Finite-State Machine (FSM) can be described as a set of relations of the form:</p> <pre> State(S) x Event(E) -> Actions(A), State(S')</pre> <p>These relations are interpreted as meaning:</p> <quote> - <p>If we are in state <c>S</c> and the event <c>E</c> occurs, we - should perform the actions <c>A</c> and make a transition to - the state <c>S'</c>.</p> + <p>If we are in state <c>S</c> and event <c>E</c> occurs, we + are to perform actions <c>A</c> and make a transition to + state <c>S'</c>.</p> </quote> <p>For an FSM implemented using the <c>gen_fsm</c> behaviour, the state transition rules are written as a number of Erlang - functions which conform to the following convention:</p> + functions, which conform to the following convention:</p> <pre> StateName(Event, StateData) -> .. code for actions here ... @@ -55,16 +56,16 @@ StateName(Event, StateData) -> <section> <title>Example</title> - <p>A door with a code lock could be viewed as an FSM. Initially, + <p>A door with a code lock can be viewed as an FSM. Initially, the door is locked. Anytime someone presses a button, this generates an event. Depending on what buttons have been pressed - before, the sequence so far may be correct, incomplete or wrong.</p> - <p>If it is correct, the door is unlocked for 30 seconds (30000 ms). + before, the sequence so far can be correct, incomplete, or wrong.</p> + <p>If it is correct, the door is unlocked for 30 seconds (30,000 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.</p> <p>Implementing the code lock FSM using <c>gen_fsm</c> results in - this callback module:</p> + the following callback module:</p> <marker id="ex"></marker> <code type="none"><![CDATA[ -module(code_lock). @@ -101,85 +102,88 @@ open(timeout, State) -> </section> <section> - <title>Starting a Gen_Fsm</title> - <p>In the example in the previous section, the gen_fsm is started by - calling <c>code_lock:start_link(Code)</c>:</p> + <title>Starting gen_fsm</title> + <p>In the example in the previous section, the <c>gen_fsm</c> is + started by calling <c>code_lock:start_link(Code)</c>:</p> <code type="none"> start_link(Code) -> gen_fsm:start_link({local, code_lock}, code_lock, lists:reverse(Code), []). </code> - <p><c>start_link</c> calls the function <c>gen_fsm:start_link/4</c>. - This function spawns and links to a new process, a gen_fsm.</p> + <p><c>start_link</c> calls the function <c>gen_fsm:start_link/4</c>, + which spawns and links to a new process, a <c>gen_fsm</c>.</p> <list type="bulleted"> <item> - <p>The first argument <c>{local, code_lock}</c> specifies - the name. In this case, the gen_fsm will be locally registered - as <c>code_lock</c>.</p> - <p>If the name is omitted, the gen_fsm is not registered. - Instead its pid must be used. The name could also be given as - <c>{global, Name}</c>, in which case the gen_fsm is registered - using <c>global:register_name/2</c>.</p> + <p>The first argument, <c>{local, code_lock}</c>, specifies + the name. In this case, the <c>gen_fsm</c> is locally + registered as <c>code_lock</c>.</p> + <p>If the name is omitted, the <c>gen_fsm</c> is not registered. + Instead its pid must be used. The name can also be given + as <c>{global, Name}</c>, in which case the <c>gen_fsm</c> is + registered using <c>global:register_name/2</c>.</p> </item> <item> <p>The second argument, <c>code_lock</c>, is the name of - the callback module, that is the module where the callback + the callback module, that is, the module where the callback functions are located.</p> - <p>In this case, the interface functions (<c>start_link</c> and - <c>button</c>) are located in the same module as the callback - functions (<c>init</c>, <c>locked</c> and <c>open</c>). This + <p>The interface functions (<c>start_link</c> and <c>button</c>) + are then located in the same module as the callback + functions (<c>init</c>, <c>locked</c>, and <c>open</c>). This is normally good programming practice, to have the code corresponding to one process contained in one module.</p> </item> <item> - <p>The third argument, <c>Code</c>, is a list of digits which is passed - reversed to the callback function <c>init</c>. Here, <c>init</c> + <p>The third argument, <c>Code</c>, is a list of digits that + which is passed reversed to the callback function <c>init</c>. + Here, <c>init</c> gets the correct code for the lock as indata.</p> </item> <item> - <p>The fourth argument, [], is a list of options. See - <c>gen_fsm(3)</c> for available options.</p> + <p>The fourth argument, <c>[]</c>, is a list of options. See + the <c>gen_fsm(3)</c> manual page for available options.</p> </item> </list> - <p>If name registration succeeds, the new gen_fsm process calls + <p>If name registration succeeds, the new <c>gen_fsm</c> process calls the callback function <c>code_lock:init(Code)</c>. This function is expected to return <c>{ok, StateName, StateData}</c>, where - <c>StateName</c> is the name of the initial state of the gen_fsm. - In this case <c>locked</c>, assuming the door is locked to begin - with. <c>StateData</c> is the internal state of the gen_fsm. (For - gen_fsms, the internal state is often referred to 'state data' to + <c>StateName</c> is the name of the initial state of the + <c>gen_fsm</c>. In this case <c>locked</c>, assuming the door is + locked to begin with. <c>StateData</c> is the internal state of + the <c>gen_fsm</c>. (For <c>gen_fsm</c>, the internal state is + often referred to 'state data' to distinguish it from the state as in states of a state machine.) In this case, the state data is the button sequence so far (empty to begin with) and the correct code of the lock.</p> <code type="none"> init(Code) -> {ok, locked, {[], Code}}.</code> - <p>Note that <c>gen_fsm:start_link</c> is synchronous. It does not - return until the gen_fsm has been initialized and is ready to + <p><c>gen_fsm:start_link</c> is synchronous. It does not return until + the <c>gen_fsm</c> has been initialized and is ready to receive notifications.</p> - <p><c>gen_fsm:start_link</c> must be used if the gen_fsm is part of - a supervision tree, i.e. is started by a supervisor. There is - another function <c>gen_fsm:start</c> to start a stand-alone - gen_fsm, i.e. a gen_fsm which is not part of a supervision tree.</p> + <p><c>gen_fsm:start_link</c> must be used if the <c>gen_fsm</c> is + part of a supervision tree, that is, started by a supervisor. There + is another function, <c>gen_fsm:start</c>, to start a standalone + <c>gen_fsm</c>, that is, a <c>gen_fsm</c> that is not part of a + supervision tree.</p> </section> <section> - <title>Notifying About Events</title> + <title>Notifying about Events</title> <p>The function notifying the code lock about a button event is implemented using <c>gen_fsm:send_event/2</c>:</p> <code type="none"> button(Digit) -> gen_fsm:send_event(code_lock, {button, Digit}).</code> - <p><c>code_lock</c> is the name of the gen_fsm and must agree with - the name used to start it. <c>{button, Digit}</c> is the actual - event.</p> - <p>The event is made into a message and sent to the gen_fsm. When - the event is received, the gen_fsm calls - <c>StateName(Event, StateData)</c> which is expected to return a - tuple <c>{next_state, StateName1, StateData1}</c>. + <p><c>code_lock</c> is the name of the <c>gen_fsm</c> and must + agree with the name used to start it. + <c>{button, Digit}</c> is the actual event.</p> + <p>The event is made into a message and sent to the <c>gen_fsm</c>. + When the event is received, the <c>gen_fsm</c> calls + <c>StateName(Event, StateData)</c>, which is expected to return a + tuple <c>{next_state,StateName1,StateData1}</c>. <c>StateName</c> is the name of the current state and <c>StateName1</c> is the name of the next state to go to. <c>StateData1</c> is a new value for the state data of - the gen_fsm.</p> + the <c>gen_fsm</c>.</p> <code type="none"><![CDATA[ locked({button, Digit}, {SoFar, Code}) -> case [Digit|SoFar] of @@ -198,20 +202,21 @@ open(timeout, State) -> <p>If the door is locked and a button is pressed, the complete button sequence so far is compared with the correct code for the lock and, depending on the result, the door is either unlocked - and the gen_fsm goes to state <c>open</c>, or the door remains in - state <c>locked</c>.</p> + and the <c>gen_fsm</c> goes to state <c>open</c>, or the door + remains in state <c>locked</c>.</p> </section> <section> - <title>Timeouts</title> + <title>Time-Outs</title> <p>When a correct code has been given, the door is unlocked and the following tuple is returned from <c>locked/2</c>:</p> <code type="none"> {next_state, open, {[], Code}, 30000};</code> - <p>30000 is a timeout value in milliseconds. After 30000 ms, i.e. - 30 seconds, a timeout occurs. Then <c>StateName(timeout, StateData)</c> is called. In this case, the timeout occurs when - the door has been in state <c>open</c> for 30 seconds. After that - the door is locked again:</p> + <p>30,000 is a time-out value in milliseconds. After this time, + that is, 30 seconds, a time-out occurs. Then, + <c>StateName(timeout, StateData)</c> is called. The time-out + then occurs when the door has been in state <c>open</c> for 30 + seconds. After that the door is locked again:</p> <code type="none"> open(timeout, State) -> do_lock(), @@ -220,7 +225,7 @@ open(timeout, State) -> <section> <title>All State Events</title> - <p>Sometimes an event can arrive at any state of the gen_fsm. + <p>Sometimes an event can arrive at any state of the <c>gen_fsm</c>. Instead of sending the message with <c>gen_fsm:send_event/2</c> and writing one clause handling the event for each state function, the message can be sent with <c>gen_fsm:send_all_state_event/2</c> @@ -245,15 +250,16 @@ handle_event(stop, _StateName, StateData) -> <section> <title>In a Supervision Tree</title> - <p>If the gen_fsm is part of a supervision tree, no stop function - is needed. The gen_fsm will automatically be terminated by its - supervisor. Exactly how this is done is defined by a - <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> + <p>If the <c>gen_fsm</c> is part of a supervision tree, no stop + function is needed. The <c>gen_fsm</c> is automatically + terminated by its supervisor. Exactly how this is done is + defined by a + <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> set in the supervisor.</p> <p>If it is necessary to clean up before termination, the shutdown - strategy must be a timeout value and the gen_fsm must be set to - trap exit signals in the <c>init</c> function. When ordered - to shutdown, the gen_fsm will then call the callback function + strategy must be a time-out value and the <c>gen_fsm</c> must be + set to trap exit signals in the <c>init</c> function. When ordered + to shutdown, the <c>gen_fsm</c> then calls the callback function <c>terminate(shutdown, StateName, StateData)</c>:</p> <code type="none"> init(Args) -> @@ -270,9 +276,9 @@ terminate(shutdown, StateName, StateData) -> </section> <section> - <title>Stand-Alone Gen_Fsms</title> - <p>If the gen_fsm is not part of a supervision tree, a stop - function may be useful, for example:</p> + <title>Standalone gen_fsm</title> + <p>If the <c>gen_fsm</c> is not part of a supervision tree, a stop + function can be useful, for example:</p> <code type="none"> ... -export([stop/0]). @@ -290,26 +296,28 @@ handle_event(stop, _StateName, StateData) -> terminate(normal, _StateName, _StateData) -> ok.</code> <p>The callback function handling the <c>stop</c> event returns a - tuple <c>{stop,normal,StateData1}</c>, where <c>normal</c> + tuple, <c>{stop,normal,StateData1}</c>, where <c>normal</c> specifies that it is a normal termination and <c>StateData1</c> - is a new value for the state data of the gen_fsm. This will - cause the gen_fsm to call + is a new value for the state data of the <c>gen_fsm</c>. This + causes the <c>gen_fsm</c> to call <c>terminate(normal,StateName,StateData1)</c> and then - terminate gracefully:</p> + it terminates gracefully:</p> </section> </section> <section> <title>Handling Other Messages</title> - <p>If the gen_fsm should be able to receive other messages than - events, the callback function <c>handle_info(Info, StateName, StateData)</c> must be implemented to handle them. Examples of - other messages are exit messages, if the gen_fsm is linked to + <p>If the <c>gen_fsm</c> is to be able to receive other messages + than events, the callback function + <c>handle_info(Info, StateName, StateData)</c> must be implemented + to handle them. Examples of + other messages are exit messages, if the <c>gen_fsm</c> is linked to other processes (than the supervisor) and trapping exit signals.</p> <code type="none"> handle_info({'EXIT', Pid, Reason}, StateName, StateData) -> ..code to handle exits here.. {next_state, StateName1, StateData1}.</code> - <p>The code_change method also has to be implemented.</p> + <p>The code_change method must also be implemented.</p> <code type="none"> code_change(OldVsn, StateName, StateData, Extra) -> ..code to convert state (and more) during code change |