diff options
author | Ingela Anderton Andin <[email protected]> | 2017-04-06 16:03:11 +0200 |
---|---|---|
committer | Ingela Anderton Andin <[email protected]> | 2017-04-27 11:07:38 +0200 |
commit | c6bdd67b33b70d862e2a2d0f582106f2e930d970 (patch) | |
tree | 6d8f9b0661466c4c36d88e8ee7666c4b8a0754c0 /system/doc/design_principles/fsm.xml | |
parent | a1100c46c2ca2121d723edc03cb7885e3014af7c (diff) | |
download | otp-c6bdd67b33b70d862e2a2d0f582106f2e930d970.tar.gz otp-c6bdd67b33b70d862e2a2d0f582106f2e930d970.tar.bz2 otp-c6bdd67b33b70d862e2a2d0f582106f2e930d970.zip |
stdlib: Deprecate gen_fsm
Diffstat (limited to 'system/doc/design_principles/fsm.xml')
-rw-r--r-- | system/doc/design_principles/fsm.xml | 338 |
1 files changed, 0 insertions, 338 deletions
diff --git a/system/doc/design_principles/fsm.xml b/system/doc/design_principles/fsm.xml deleted file mode 100644 index 4f2b75e6e8..0000000000 --- a/system/doc/design_principles/fsm.xml +++ /dev/null @@ -1,338 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE chapter SYSTEM "chapter.dtd"> - -<chapter> - <header> - <copyright> - <year>1997</year><year>2016</year> - <holder>Ericsson AB. All Rights Reserved.</holder> - </copyright> - <legalnotice> - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - </legalnotice> - - <title>gen_fsm Behaviour</title> - <prepared></prepared> - <docno></docno> - <date></date> - <rev></rev> - <file>fsm.xml</file> - </header> - <marker id="gen_fsm behaviour"></marker> - <note> - <p> - There is a new behaviour - <seealso marker="statem"><c>gen_statem</c></seealso> - that is intended to replace <c>gen_fsm</c> for new code. - It has the same features and add some really useful. - This module will not be removed for the foreseeable future - to keep old state machine implementations running. - </p> - </note> - <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 - 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 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> - <pre> -StateName(Event, StateData) -> - .. code for actions here ... - {next_state, StateName', StateData'}</pre> - </section> - - <section> - <title>Example</title> - <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 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 - the following callback module:</p> - <marker id="ex"></marker> - <code type="none"><![CDATA[ --module(code_lock). --behaviour(gen_fsm). - --export([start_link/1]). --export([button/1]). --export([init/1, locked/2, open/2]). - -start_link(Code) -> - gen_fsm:start_link({local, code_lock}, code_lock, lists:reverse(Code), []). - -button(Digit) -> - gen_fsm:send_event(code_lock, {button, Digit}). - -init(Code) -> - {ok, locked, {[], Code}}. - -locked({button, Digit}, {SoFar, Code}) -> - case [Digit|SoFar] of - Code -> - do_unlock(), - {next_state, open, {[], Code}, 30000}; - Incomplete when length(Incomplete)<length(Code) -> - {next_state, locked, {Incomplete, Code}}; - _Wrong -> - {next_state, locked, {[], Code}} - end. - -open(timeout, State) -> - do_lock(), - {next_state, locked, State}.]]></code> - <p>The code is explained in the next sections.</p> - </section> - - <section> - <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>, - 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 <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 - functions are located.</p> - <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 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, <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 <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 - <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><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 <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> - <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 <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 <c>gen_fsm</c>.</p> - <code type="none"><![CDATA[ -locked({button, Digit}, {SoFar, Code}) -> - case [Digit|SoFar] of - Code -> - do_unlock(), - {next_state, open, {[], Code}, 30000}; - Incomplete when length(Incomplete)<length(Code) -> - {next_state, locked, {Incomplete, Code}}; - _Wrong -> - {next_state, locked, {[], Code}}; - end. - -open(timeout, State) -> - do_lock(), - {next_state, locked, State}.]]></code> - <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 <c>gen_fsm</c> goes to state <c>open</c>, or the door - remains in state <c>locked</c>.</p> - </section> - - <section> - <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>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(), - {next_state, locked, State}.</code> - </section> - - <section> - <title>All State Events</title> - <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> - and handled with <c>Module:handle_event/3</c>:</p> - <code type="none"> --module(code_lock). -... --export([stop/0]). -... - -stop() -> - gen_fsm:send_all_state_event(code_lock, stop). - -... - -handle_event(stop, _StateName, StateData) -> - {stop, normal, StateData}.</code> - </section> - - <section> - <title>Stopping</title> - - <section> - <title>In a Supervision Tree</title> - <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 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) -> - ..., - process_flag(trap_exit, true), - ..., - {ok, StateName, StateData}. - -... - -terminate(shutdown, StateName, StateData) -> - ..code for cleaning up here.. - ok.</code> - </section> - - <section> - <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]). -... - -stop() -> - gen_fsm:send_all_state_event(code_lock, stop). -... - -handle_event(stop, _StateName, StateData) -> - {stop, normal, 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> - specifies that it is a normal termination and <c>StateData1</c> - 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 - it terminates gracefully:</p> - </section> - </section> - - <section> - <title>Handling Other Messages</title> - <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 must also be implemented.</p> - <code type="none"> -code_change(OldVsn, StateName, StateData, Extra) -> - ..code to convert state (and more) during code change - {ok, NextStateName, NewStateData}</code> - </section> -</chapter> - |