aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/doc/src/gen_fsm.xml
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/doc/src/gen_fsm.xml')
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml973
1 files changed, 161 insertions, 812 deletions
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 4d594b8eb2..7187630b43 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -4,14 +4,14 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2014</year>
+ <year>1996-2017</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
@@ -29,827 +29,176 @@
<rev></rev>
</header>
<module>gen_fsm</module>
- <modulesummary>Generic Finite State Machine Behaviour</modulesummary>
+ <modulesummary>Deprecated and replaced by gen_statem </modulesummary>
+
<description>
- <p>A behaviour module for implementing a finite state machine.
- A generic finite state machine process (gen_fsm) 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
- <seealso marker="doc/design_principles:fsm">OTP Design Principles</seealso> for more information.</p>
- <p>A gen_fsm 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:</p>
- <pre>
-gen_fsm module Callback module
--------------- ---------------
-gen_fsm:start
-gen_fsm:start_link -----> Module:init/1
+ <p> Deprecated and replaced by <seealso marker="gen_statem"><c>gen_statem</c></seealso> </p>
+ </description>
+ <section>
+ <marker id="Migration to gen_statem"/>
+ <title>Migration to gen_statem</title>
+
+ <p>Here follows a simple example of turning a gen_fsm into
+ a <seealso marker="gen_statem"><c>gen_statem</c></seealso>. The example comes
+ from the previous Users Guide for <c>gen_fsm</c> </p>
+
+ <code type="erl">
+-module(code_lock).
+-define(NAME, code_lock).
+%-define(BEFORE_REWRITE, true).
-gen_fsm:stop -----> Module:terminate/3
+-ifdef(BEFORE_REWRITE).
+-behaviour(gen_fsm).
+-else.
+-behaviour(gen_statem).
+-endif.
-gen_fsm:send_event -----> Module:StateName/2
+-export([start_link/1, button/1, stop/0]).
-gen_fsm:send_all_state_event -----> Module:handle_event/3
+-ifdef(BEFORE_REWRITE).
+-export([init/1, locked/2, open/2, handle_sync_event/4, handle_event/3,
+ handle_info/3, terminate/3, code_change/4]).
+-else.
+-export([init/1, callback_mode/0, locked/3, open/3, terminate/3, code_change/4]).
+%% Add callback__mode/0
+%% Change arity of the state functions
+%% Remove handle_info/3
+-endif.
-gen_fsm:sync_send_event -----> Module:StateName/3
+-ifdef(BEFORE_REWRITE).
+start_link(Code) ->
+ gen_fsm:start_link({local, ?NAME}, ?MODULE, Code, []).
+-else.
+start_link(Code) ->
+ gen_statem:start_link({local,?NAME}, ?MODULE, Code, []).
+-endif.
-gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
+-ifdef(BEFORE_REWRITE).
+button(Digit) ->
+ gen_fsm:send_event(?NAME, {button, Digit}).
+-else.
+button(Digit) ->
+ gen_statem:cast(?NAME, {button,Digit}).
+ %% send_event is asynchronous and becomes a cast
+-endif.
-- -----> Module:handle_info/3
+-ifdef(BEFORE_REWRITE).
+stop() ->
+ gen_fsm:sync_send_all_state_event(?NAME, stop).
+-else.
+stop() ->
+ gen_statem:call(?NAME, stop).
+ %% sync_send is synchronous and becomes call
+ %% all_state is handled by callback code in gen_statem
+-endif.
-- -----> Module:terminate/3
+init(Code) ->
+ do_lock(),
+ Data = #{code => Code, remaining => Code},
+ {ok, locked, Data}.
-- -----> Module:code_change/4</pre>
- <p>If a callback function fails or returns a bad value, the gen_fsm
- will terminate.</p>
- <p>A gen_fsm handles system messages as documented in
- <seealso marker="sys">sys(3)</seealso>. The <c>sys</c> module
- can be used for debugging a gen_fsm.</p>
- <p>Note that a gen_fsm does not trap exit signals automatically,
- this must be explicitly initiated in the callback module.</p>
- <p>Unless otherwise stated, all functions in this module fail if
- the specified gen_fsm does not exist or if bad arguments are
- given.</p>
- <p>The gen_fsm process can go into hibernation
- (see <seealso marker="erts:erlang#erlang:hibernate/3">erlang(3)</seealso>) if a callback
- function specifies <c>'hibernate'</c> instead of a timeout value. This
- might be useful if the server is expected to be idle for a long
- time. However this feature should be used with care as hibernation
- implies at least two garbage collections (when hibernating and
- shortly after waking up) and is not something you'd want to do
- between each call to a busy state machine.</p>
+-ifdef(BEFORE_REWRITE).
+-else.
+callback_mode() ->
+ state_functions.
+%% state_functions mode is the mode most similar to
+%% gen_fsm. There is also handle_event mode which is
+%% a fairly different concept.
+-endif.
- </description>
- <funcs>
- <func>
- <name>start_link(Module, Args, Options) -> Result</name>
- <name>start_link(FsmName, Module, Args, Options) -> Result</name>
- <fsummary>Create a gen_fsm process in a supervision tree.</fsummary>
- <type>
- <v>FsmName = {local,Name} | {global,GlobalName}
- | {via,Module,ViaName}</v>
- <v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Module = atom()</v>
- <v>Args = term()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
- <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
- <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
- <v>&nbsp;&nbsp;SOpts = [SOpt]</v>
- <v>&nbsp;&nbsp;&nbsp;SOpt - see erlang:spawn_opt/2,3,4,5</v>
- <v>Result = {ok,Pid} | ignore | {error,Error}</v>
- <v>&nbsp;Pid = pid()</v>
- <v>&nbsp;Error = {already_started,Pid} | term()</v>
- </type>
- <desc>
- <p>Creates a gen_fsm process as part of a supervision tree.
- The function should be called, directly or indirectly, by
- the supervisor. It will, among other things, ensure that
- the gen_fsm is linked to the supervisor.</p>
- <p>The gen_fsm process calls <c>Module:init/1</c> to
- initialize. To ensure a synchronized start-up procedure,
- <c>start_link/3,4</c> does not return until
- <c>Module:init/1</c> has returned.</p>
- <p>If <c>FsmName={local,Name}</c>, the gen_fsm is registered
- locally as <c>Name</c> using <c>register/2</c>.
- If <c>FsmName={global,GlobalName}</c>, the gen_fsm is
- registered globally as <c>GlobalName</c> using
- <c>global:register_name/2</c>.
- If <c>FsmName={via,Module,ViaName}</c>, the gen_fsm will
- register with the registry represented by <c>Module</c>.
- The <c>Module</c> callback should export the functions
- <c>register_name/2</c>, <c>unregister_name/1</c>,
- <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the
- corresponding functions in <c>global</c>. Thus,
- <c>{via,global,GlobalName}</c> is a valid reference.</p>
- <p>If no name is provided,
- the gen_fsm is not registered.</p>
- <p><c>Module</c> is the name of the callback module.</p>
- <p><c>Args</c> is an arbitrary term which is passed as
- the argument to <c>Module:init/1</c>.</p>
- <p>If the option <c>{timeout,Time}</c> is present, the gen_fsm
- is allowed to spend <c>Time</c> milliseconds initializing
- or it will be terminated and the start function will return
- <c>{error,timeout}</c>.</p>
- <p>If the option <c>{debug,Dbgs}</c> is present,
- the corresponding <c>sys</c> function will be called for each
- item in <c>Dbgs</c>. See
- <seealso marker="sys">sys(3)</seealso>.</p>
- <p>If the option <c>{spawn_opt,SOpts}</c> is present,
- <c>SOpts</c> will be passed as option list to
- the <c>spawn_opt</c> BIF which is used to spawn the gen_fsm
- process. See
- <seealso marker="erts:erlang#spawn_opt/2">erlang(3)</seealso>.</p>
- <note>
- <p>Using the spawn option <c>monitor</c> is currently not
- allowed, but will cause the function to fail with reason
- <c>badarg</c>.</p>
- </note>
- <p>If the gen_fsm is successfully created and initialized
- the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is
- the pid of the gen_fsm. If there already exists a process with
- the specified <c>FsmName</c>, the function returns
- <c>{error,{already_started,Pid}}</c> where <c>Pid</c> is
- the pid of that process.</p>
- <p>If <c>Module:init/1</c> fails with <c>Reason</c>,
- the function returns <c>{error,Reason}</c>. If
- <c>Module:init/1</c> returns <c>{stop,Reason}</c> or
- <c>ignore</c>, the process is terminated and the function
- returns <c>{error,Reason}</c> or <c>ignore</c>, respectively.</p>
- </desc>
- </func>
- <func>
- <name>start(Module, Args, Options) -> Result</name>
- <name>start(FsmName, Module, Args, Options) -> Result</name>
- <fsummary>Create a stand-alone gen_fsm process.</fsummary>
- <type>
- <v>FsmName = {local,Name} | {global,GlobalName}
- | {via,Module,ViaName}</v>
- <v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Module = atom()</v>
- <v>Args = term()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v>
- <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
- <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
- <v>&nbsp;&nbsp;SOpts = [term()]</v>
- <v>Result = {ok,Pid} | ignore | {error,Error}</v>
- <v>&nbsp;Pid = pid()</v>
- <v>&nbsp;Error = {already_started,Pid} | term()</v>
- </type>
- <desc>
- <p>Creates a stand-alone gen_fsm process, i.e. a gen_fsm which
- is not part of a supervision tree and thus has no supervisor.</p>
- <p>See <seealso marker="#start_link/3">start_link/3,4</seealso>
- for a description of arguments and return values.</p>
- </desc>
- </func>
- <func>
- <name>stop(FsmRef) -> ok</name>
- <name>stop(FsmRef, Reason, Timeout) -> ok</name>
- <fsummary>Synchronously stop a generic FSM.</fsummary>
- <type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
- | {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Node = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Reason = term()</v>
- <v>Timeout = int()>0 | infinity</v>
- </type>
- <desc>
- <p>Orders a generic FSM to exit with the given <c>Reason</c>
- and waits for it to terminate. The gen_fsm will call
- <seealso marker="#Module:terminate/3">Module:terminate/3</seealso>
- before exiting.</p>
- <p>The function returns <c>ok</c> if the generic FSM terminates
- with the expected reason. Any other reason than <c>normal</c>,
- <c>shutdown</c>, or <c>{shutdown,Term}</c> will cause an
- error report to be issued using
- <seealso marker="kernel:error_logger#format/2">error_logger:format/2</seealso>.
- The default <c>Reason</c> is <c>normal</c>.</p>
- <p><c>Timeout</c> is an integer greater than zero which
- specifies how many milliseconds to wait for the generic FSM
- to terminate, or the atom <c>infinity</c> to wait
- indefinitely. The default value is <c>infinity</c>. If the
- generic FSM has not terminated within the specified time, a
- <c>timeout</c> exception is raised.</p>
- <p>If the process does not exist, a <c>noproc</c> exception
- is raised.</p>
- </desc>
- </func>
- <func>
- <name>send_event(FsmRef, Event) -> ok</name>
- <fsummary>Send an event asynchronously to a generic FSM.</fsummary>
- <type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
- | {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Event = term()</v>
- </type>
- <desc>
- <p>Sends an event asynchronously to the gen_fsm <c>FsmRef</c>
- and returns <c>ok</c> immediately. The gen_fsm will call
- <c>Module:StateName/2</c> to handle the event, where
- <c>StateName</c> is the name of the current state of
- the gen_fsm.</p>
- <p><c>FsmRef</c> can be:</p>
- <list type="bulleted">
- <item>the pid,</item>
- <item><c>Name</c>, if the gen_fsm is locally registered,</item>
- <item><c>{Name,Node}</c>, if the gen_fsm is locally
- registered at another node, or</item>
- <item><c>{global,GlobalName}</c>, if the gen_fsm is globally
- registered.</item>
- <item><c>{via,Module,ViaName}</c>, if the gen_fsm is registered
- through an alternative process registry.</item>
- </list>
- <p><c>Event</c> is an arbitrary term which is passed as one of
- the arguments to <c>Module:StateName/2</c>.</p>
- </desc>
- </func>
- <func>
- <name>send_all_state_event(FsmRef, Event) -> ok</name>
- <fsummary>Send an event asynchronously to a generic FSM.</fsummary>
- <type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
- | {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Event = term()</v>
- </type>
- <desc>
- <p>Sends an event asynchronously to the gen_fsm <c>FsmRef</c>
- and returns <c>ok</c> immediately. The gen_fsm will call
- <c>Module:handle_event/3</c> to handle the event.</p>
- <p>See <seealso marker="#send_event/2">send_event/2</seealso>
- for a description of the arguments.</p>
- <p>The difference between <c>send_event</c> and
- <c>send_all_state_event</c> is which callback function is
- used to handle the event. This function is useful when
- sending events that are handled the same way in every state,
- as only one <c>handle_event</c> clause is needed to handle
- the event instead of one clause in each state name function.</p>
- </desc>
- </func>
- <func>
- <name>sync_send_event(FsmRef, Event) -> Reply</name>
- <name>sync_send_event(FsmRef, Event, Timeout) -> Reply</name>
- <fsummary>Send an event synchronously to a generic FSM.</fsummary>
- <type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
- | {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Event = term()</v>
- <v>Timeout = int()>0 | infinity</v>
- <v>Reply = term()</v>
- </type>
- <desc>
- <p>Sends an event to the gen_fsm <c>FsmRef</c> and waits until a
- reply arrives or a timeout occurs. The gen_fsm will call
- <c>Module:StateName/3</c> to handle the event, where
- <c>StateName</c> is the name of the current state of
- the gen_fsm.</p>
- <p>See <seealso marker="#send_event/2">send_event/2</seealso>
- for a description of <c>FsmRef</c> and <c>Event</c>.</p>
- <p><c>Timeout</c> is an integer greater than zero which
- specifies how many milliseconds to wait for a reply, or
- the atom <c>infinity</c> to wait indefinitely. Default value
- is 5000. If no reply is received within the specified time,
- the function call fails.</p>
- <p>The return value <c>Reply</c> is defined in the return value
- of <c>Module:StateName/3</c>.</p>
- <p>The ancient behaviour of sometimes consuming the server
- exit message if the server died during the call while
- linked to the client has been removed in OTP R12B/Erlang 5.6.</p>
- </desc>
- </func>
- <func>
- <name>sync_send_all_state_event(FsmRef, Event) -> Reply</name>
- <name>sync_send_all_state_event(FsmRef, Event, Timeout) -> Reply</name>
- <fsummary>Send an event synchronously to a generic FSM.</fsummary>
- <type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
- | {via,Module,ViaName} | pid()</v>
- <v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Event = term()</v>
- <v>Timeout = int()>0 | infinity</v>
- <v>Reply = term()</v>
- </type>
- <desc>
- <p>Sends an event to the gen_fsm <c>FsmRef</c> and waits until a
- reply arrives or a timeout occurs. The gen_fsm will call
- <c>Module:handle_sync_event/4</c> to handle the event.</p>
- <p>See <seealso marker="#send_event/2">send_event/2</seealso>
- for a description of <c>FsmRef</c> and <c>Event</c>. See
- <seealso marker="#sync_send_event/3">sync_send_event/3</seealso>
- for a description of <c>Timeout</c> and <c>Reply</c>.</p>
- <p>See
- <seealso marker="#send_all_state_event/2">send_all_state_event/2</seealso>
- for a discussion about the difference between
- <c>sync_send_event</c> and <c>sync_send_all_state_event</c>.</p>
- </desc>
- </func>
- <func>
- <name>reply(Caller, Reply) -> Result</name>
- <fsummary>Send a reply to a caller.</fsummary>
- <type>
- <v>Caller - see below</v>
- <v>Reply = term()</v>
- <v>Result = term()</v>
- </type>
- <desc>
- <p>This function can be used by a gen_fsm to explicitly send a
- reply to a client process that called
- <seealso marker="#sync_send_event/2">sync_send_event/2,3</seealso>
- or
- <seealso marker="#sync_send_all_state_event/2">sync_send_all_state_event/2,3</seealso>,
- when the reply cannot be defined in the return value of
- <c>Module:State/3</c> or <c>Module:handle_sync_event/4</c>.</p>
- <p><c>Caller</c> must be the <c>From</c> argument provided to
- the callback function. <c>Reply</c> is an arbitrary term,
- which will be given back to the client as the return value of
- <c>sync_send_event/2,3</c> or
- <c>sync_send_all_state_event/2,3</c>.</p>
- <p>The return value <c>Result</c> is not further defined, and
- should always be ignored.</p>
- </desc>
- </func>
- <func>
- <name>send_event_after(Time, Event) -> Ref</name>
- <fsummary>Send a delayed event internally in a generic FSM.</fsummary>
- <type>
- <v>Time = integer()</v>
- <v>Event = term()</v>
- <v>Ref = reference()</v>
- </type>
- <desc>
- <p>Sends a delayed event internally in the gen_fsm that calls
- this function after <c>Time</c> ms. Returns immediately a
- reference that can be used to cancel the delayed send using
- <seealso marker="#cancel_timer/1">cancel_timer/1</seealso>.</p>
- <p>The gen_fsm will call <c>Module:StateName/2</c> to handle
- the event, where <c>StateName</c> is the name of the current
- state of the gen_fsm at the time the delayed event is
- delivered.</p>
- <p><c>Event</c> is an arbitrary term which is passed as one of
- the arguments to <c>Module:StateName/2</c>.</p>
- </desc>
- </func>
- <func>
- <name>start_timer(Time, Msg) -> Ref</name>
- <fsummary>Send a timeout event internally in a generic FSM.</fsummary>
- <type>
- <v>Time = integer()</v>
- <v>Msg = term()</v>
- <v>Ref = reference()</v>
- </type>
- <desc>
- <p>Sends a timeout event internally in the gen_fsm that calls
- this function after <c>Time</c> ms. Returns immediately a
- reference that can be used to cancel the timer using
- <seealso marker="#cancel_timer/1">cancel_timer/1</seealso>.</p>
- <p>The gen_fsm will call <c>Module:StateName/2</c> to handle
- the event, where <c>StateName</c> is the name of the current
- state of the gen_fsm at the time the timeout message is
- delivered.</p>
- <p><c>Msg</c> is an arbitrary term which is passed in the
- timeout message, <c>{timeout, Ref, Msg}</c>, as one of
- the arguments to <c>Module:StateName/2</c>.</p>
- </desc>
- </func>
- <func>
- <name>cancel_timer(Ref) -> RemainingTime | false</name>
- <fsummary>Cancel an internal timer in a generic FSM.</fsummary>
- <type>
- <v>Ref = reference()</v>
- <v>RemainingTime = integer()</v>
- </type>
- <desc>
- <p>Cancels an internal timer referred by <c>Ref</c> in the
- gen_fsm that calls this function.</p>
- <p><c>Ref</c> is a reference returned from
- <seealso marker="#send_event_after/2">send_event_after/2</seealso>
- or
- <seealso marker="#start_timer/2">start_timer/2</seealso>.</p>
- <p>If the timer has already timed out, but the event not yet
- been delivered, it is cancelled as if it had <em>not</em>
- timed out, so there will be no false timer event after
- returning from this function.</p>
- <p>Returns the remaining time in ms until the timer would
- have expired if <c>Ref</c> referred to an active timer,
- <c>false</c> otherwise.</p>
- </desc>
- </func>
- <func>
- <name>enter_loop(Module, Options, StateName, StateData)</name>
- <name>enter_loop(Module, Options, StateName, StateData, FsmName)</name>
- <name>enter_loop(Module, Options, StateName, StateData, Timeout)</name>
- <name>enter_loop(Module, Options, StateName, StateData, FsmName, Timeout)</name>
- <fsummary>Enter the gen_fsm receive loop</fsummary>
- <type>
- <v>Module = atom()</v>
- <v>Options = [Option]</v>
- <v>&nbsp;Option = {debug,Dbgs}</v>
- <v>&nbsp;&nbsp;Dbgs = [Dbg]</v>
- <v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
- <v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
- <v>StateName = atom()</v>
- <v>StateData = term()</v>
- <v>FsmName = {local,Name} | {global,GlobalName}
- | {via,Module,ViaName}</v>
- <v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = ViaName = term()</v>
- <v>Timeout = int() | infinity</v>
- </type>
- <desc>
- <p>Makes an existing process into a gen_fsm. Does not return,
- instead the calling process will enter the gen_fsm receive
- loop and become a gen_fsm process. The process <em>must</em>
- have been started using one of the start functions in
- <c>proc_lib</c>, see
- <seealso marker="proc_lib">proc_lib(3)</seealso>. The user is
- responsible for any initialization of the process, including
- registering a name for it.</p>
- <p>This function is useful when a more complex initialization
- procedure is needed than the gen_fsm behaviour provides.</p>
- <p><c>Module</c>, <c>Options</c> and <c>FsmName</c> have
- the same meanings as when calling
- <seealso marker="#start_link/3">start[_link]/3,4</seealso>.
- However, if <c>FsmName</c> is specified, the process must have
- been registered accordingly <em>before</em> this function is
- called.</p>
- <p><c>StateName</c>, <c>StateData</c> and <c>Timeout</c> have
- the same meanings as in the return value of
- <seealso marker="#Moduleinit">Module:init/1</seealso>.
- Also, the callback module <c>Module</c> does not need to
- export an <c>init/1</c> function.</p>
- <p>Failure: If the calling process was not started by a
- <c>proc_lib</c> start function, or if it is not registered
- according to <c>FsmName</c>.</p>
- </desc>
- </func>
- </funcs>
+-ifdef(BEFORE_REWRITE).
+locked({button, Digit}, Data0) ->
+ case analyze_lock(Digit, Data0) of
+ {open = StateName, Data} ->
+ {next_state, StateName, Data, 10000};
+ {StateName, Data} ->
+ {next_state, StateName, Data}
+ end.
+-else.
+locked(cast, {button,Digit}, Data0) ->
+ case analyze_lock(Digit, Data0) of
+ {open = StateName, Data} ->
+ {next_state, StateName, Data, 10000};
+ {StateName, Data} ->
+ {next_state, StateName, Data}
+ end;
+locked({call, From}, Msg, Data) ->
+ handle_call(From, Msg, Data);
+locked({info, Msg}, StateName, Data) ->
+ handle_info(Msg, StateName, Data).
+%% Arity differs
+%% All state events are dispatched to handle_call and handle_info help
+%% functions. If you want to handle a call or cast event specifically
+%% for this state you would add a special clause for it above.
+-endif.
- <section>
- <title>CALLBACK FUNCTIONS</title>
- <p>The following functions should be exported from a <c>gen_fsm</c>
- callback module.</p>
- <p>In the description, the expression <em>state name</em> is used to
- denote a state of the state machine. <em>state data</em> is used
- to denote the internal state of the Erlang process which
- implements the state machine.</p>
- </section>
- <funcs>
- <func>
- <name>Module:init(Args) -> Result</name>
- <fsummary>Initialize process and internal state name and state data.</fsummary>
- <type>
- <v>Args = term()</v>
- <v>Result = {ok,StateName,StateData} | {ok,StateName,StateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {ok,StateName,StateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason} | ignore</v>
- <v>&nbsp;StateName = atom()</v>
- <v>&nbsp;StateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;Reason = term()</v>
- </type>
- <desc>
- <marker id="Moduleinit"></marker>
- <p>Whenever a gen_fsm is started using
- <seealso marker="#start/3">gen_fsm:start/3,4</seealso> or
- <seealso marker="#start_link/3">gen_fsm:start_link/3,4</seealso>,
- this function is called by the new process to initialize.</p>
- <p><c>Args</c> is the <c>Args</c> argument provided to the start
- function.</p>
- <p>If initialization is successful, the function should return
- <c>{ok,StateName,StateData}</c>,
- <c>{ok,StateName,StateData,Timeout}</c> or <c>{ok,StateName,StateData,hibernate}</c>,
- where <c>StateName</c>
- is the initial state name and <c>StateData</c> the initial
- state data of the gen_fsm.</p>
- <p>If an integer timeout value is provided, a timeout will occur
- unless an event or a message is received within <c>Timeout</c>
- milliseconds. A timeout is represented by the atom
- <c>timeout</c> and should be handled by
- the <c>Module:StateName/2</c> callback functions. The atom
- <c>infinity</c> can be used to wait indefinitely, this is
- the default value.</p>
- <p>If <c>hibernate</c> is specified instead of a timeout value, the process will go
- into hibernation when waiting for the next message to arrive (by calling
- <seealso marker="proc_lib#hibernate/3">proc_lib:hibernate/3</seealso>).</p>
- <p>If something goes wrong during the initialization
- the function should return <c>{stop,Reason}</c>, where
- <c>Reason</c> is any term, or <c>ignore</c>.</p>
- </desc>
- </func>
- <func>
- <name>Module:StateName(Event, StateData) -> Result</name>
- <fsummary>Handle an asynchronous event.</fsummary>
- <type>
- <v>Event = timeout | term()</v>
- <v>StateData = term()</v>
- <v>Result = {next_state,NextStateName,NewStateData} </v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,NewStateData}</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;Reason = term()</v>
- </type>
- <desc>
- <p>There should be one instance of this function for each
- possible state name. Whenever a gen_fsm receives an event
- sent using
- <seealso marker="#send_event/2">gen_fsm:send_event/2</seealso>,
- the instance of this function with the same name as
- the current state name <c>StateName</c> is called to handle
- the event. It is also called if a timeout occurs.</p>
- <p><c>Event</c> is either the atom <c>timeout</c>, if a timeout
- has occurred, or the <c>Event</c> argument provided to
- <c>send_event/2</c>.</p>
- <p><c>StateData</c> is the state data of the gen_fsm.</p>
- <p>If the function returns
- <c>{next_state,NextStateName,NewStateData}</c>,
- <c>{next_state,NextStateName,NewStateData,Timeout}</c> or
- <c>{next_state,NextStateName,NewStateData,hibernate}</c>,
- the gen_fsm will continue executing with the current state
- name set to <c>NextStateName</c> and with the possibly
- updated state data <c>NewStateData</c>. See
- <c>Module:init/1</c> for a description of <c>Timeout</c> and <c>hibernate</c>.</p>
- <p>If the function returns <c>{stop,Reason,NewStateData}</c>,
- the gen_fsm will call
- <c>Module:terminate(Reason,StateName,NewStateData)</c> and
- terminate.</p>
- </desc>
- </func>
- <func>
- <name>Module:handle_event(Event, StateName, StateData) -> Result</name>
- <fsummary>Handle an asynchronous event.</fsummary>
- <type>
- <v>Event = term()</v>
- <v>StateName = atom()</v>
- <v>StateData = term()</v>
- <v>Result = {next_state,NextStateName,NewStateData} </v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,NewStateData}</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;Reason = term()</v>
- </type>
- <desc>
- <p>Whenever a gen_fsm receives an event sent using
- <seealso marker="#send_all_state_event/2">gen_fsm:send_all_state_event/2</seealso>,
- this function is called to handle the event.</p>
- <p><c>StateName</c> is the current state name of the gen_fsm.</p>
- <p>See <c>Module:StateName/2</c> for a description of the other
- arguments and possible return values.</p>
- </desc>
- </func>
- <func>
- <name>Module:StateName(Event, From, StateData) -> Result</name>
- <fsummary>Handle a synchronous event.</fsummary>
- <type>
- <v>Event = term()</v>
- <v>From = {pid(),Tag}</v>
- <v>StateData = term()</v>
- <v>Result = {reply,Reply,NextStateName,NewStateData}</v>
- <v>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}</v>
- <v>&nbsp;Reply = term()</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;Reason = normal | term()</v>
- </type>
- <desc>
- <p>There should be one instance of this function for each
- possible state name. Whenever a gen_fsm receives an event
- sent using
- <seealso marker="#sync_send_event/2">gen_fsm:sync_send_event/2,3</seealso>,
- the instance of this function with the same name as
- the current state name <c>StateName</c> is called to handle
- the event.</p>
- <p><c>Event</c> is the <c>Event</c> argument provided to
- <c>sync_send_event</c>.</p>
- <p><c>From</c> is a tuple <c>{Pid,Tag}</c> where <c>Pid</c> is
- the pid of the process which called <c>sync_send_event/2,3</c>
- and <c>Tag</c> is a unique tag.</p>
- <p><c>StateData</c> is the state data of the gen_fsm.</p>
- <p>If the function returns
- <c>{reply,Reply,NextStateName,NewStateData}</c>,
- <c>{reply,Reply,NextStateName,NewStateData,Timeout}</c> or
- <c>{reply,Reply,NextStateName,NewStateData,hibernate}</c>,
- <c>Reply</c> will be given back to <c>From</c> as the return
- value of <c>sync_send_event/2,3</c>. The gen_fsm then
- continues executing with the current state name set to
- <c>NextStateName</c> and with the possibly updated state data
- <c>NewStateData</c>. See <c>Module:init/1</c> for a
- description of <c>Timeout</c> and <c>hibernate</c>.</p>
- <p>If the function returns
- <c>{next_state,NextStateName,NewStateData}</c>,
- <c>{next_state,NextStateName,NewStateData,Timeout}</c> or
- <c>{next_state,NextStateName,NewStateData,hibernate}</c>,
- the gen_fsm will continue executing in <c>NextStateName</c>
- with <c>NewStateData</c>. Any reply to <c>From</c> must be
- given explicitly using
- <seealso marker="#reply/2">gen_fsm:reply/2</seealso>.</p>
- <p>If the function returns
- <c>{stop,Reason,Reply,NewStateData}</c>, <c>Reply</c> will be
- given back to <c>From</c>. If the function returns
- <c>{stop,Reason,NewStateData}</c>, any reply to <c>From</c>
- must be given explicitly using <c>gen_fsm:reply/2</c>.
- The gen_fsm will then call
- <c>Module:terminate(Reason,StateName,NewStateData)</c> and
- terminate.</p>
- </desc>
- </func>
- <func>
- <name>Module:handle_sync_event(Event, From, StateName, StateData) -> Result</name>
- <fsummary>Handle a synchronous event.</fsummary>
- <type>
- <v>Event = term()</v>
- <v>From = {pid(),Tag}</v>
- <v>StateName = atom()</v>
- <v>StateData = term()</v>
- <v>Result = {reply,Reply,NextStateName,NewStateData}</v>
- <v>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {reply,Reply,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,Reply,NewStateData} | {stop,Reason,NewStateData}</v>
- <v>&nbsp;Reply = term()</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;Reason = term()</v>
- </type>
- <desc>
- <p>Whenever a gen_fsm receives an event sent using
- <seealso marker="#sync_send_all_state_event/2">gen_fsm:sync_send_all_state_event/2,3</seealso>,
- this function is called to handle the event.</p>
- <p><c>StateName</c> is the current state name of the gen_fsm.</p>
- <p>See <c>Module:StateName/3</c> for a description of the other
- arguments and possible return values.</p>
- </desc>
- </func>
- <func>
- <name>Module:handle_info(Info, StateName, StateData) -> Result</name>
- <fsummary>Handle an incoming message.</fsummary>
- <type>
- <v>Info = term()</v>
- <v>StateName = atom()</v>
- <v>StateData = term()</v>
- <v>Result = {next_state,NextStateName,NewStateData}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,Timeout}</v>
- <v>&nbsp;&nbsp;| {next_state,NextStateName,NewStateData,hibernate}</v>
- <v>&nbsp;&nbsp;| {stop,Reason,NewStateData}</v>
- <v>&nbsp;NextStateName = atom()</v>
- <v>&nbsp;NewStateData = term()</v>
- <v>&nbsp;Timeout = int()>0 | infinity</v>
- <v>&nbsp;Reason = normal | term()</v>
- </type>
- <desc>
- <p>This function is called by a gen_fsm when it receives any
- other message than a synchronous or asynchronous event (or a
- system message).</p>
- <p><c>Info</c> is the received message.</p>
- <p>See <c>Module:StateName/2</c> for a description of the other
- arguments and possible return values.</p>
- </desc>
- </func>
- <func>
- <name>Module:terminate(Reason, StateName, StateData)</name>
- <fsummary>Clean up before termination.</fsummary>
- <type>
- <v>Reason = normal | shutdown | {shutdown,term()} | term()</v>
- <v>StateName = atom()</v>
- <v>StateData = term()</v>
- </type>
- <desc>
- <p>This function is called by a gen_fsm when it is about to
- terminate. It should be the opposite of <c>Module:init/1</c>
- and do any necessary cleaning up. When it returns, the gen_fsm
- terminates with <c>Reason</c>. The return value is ignored.</p>
- <p><c>Reason</c> is a term denoting the stop reason,
- <c>StateName</c> is the current state name, and
- <c>StateData</c> is the state data of the gen_fsm.</p>
- <p><c>Reason</c> depends on why the gen_fsm is terminating. If
- it is because another callback function has returned a stop
- tuple <c>{stop,..}</c>, <c>Reason</c> will have the value
- specified in that tuple. If it is due to a failure,
- <c>Reason</c> is the error reason.</p>
- <p>If the gen_fsm is part of a supervision tree and is ordered
- by its supervisor to terminate, this function will be called
- with <c>Reason=shutdown</c> if the following conditions apply:</p>
- <list type="bulleted">
- <item>the gen_fsm has been set to trap exit signals, and</item>
- <item>the shutdown strategy as defined in the supervisor's
- child specification is an integer timeout value, not
- <c>brutal_kill</c>.</item>
- </list>
- <p>Even if the gen_fsm is <em>not</em> part of a supervision tree,
- this function will be called if it receives an <c>'EXIT'</c>
- message from its parent. <c>Reason</c> will be the same as in
- the <c>'EXIT'</c> message.</p>
- <p>Otherwise, the gen_fsm will be immediately terminated.</p>
- <p>Note that for any other reason than <c>normal</c>,
- <c>shutdown</c>, or <c>{shutdown,Term}</c> the gen_fsm is
- assumed to terminate due to an error and
- an error report is issued using
- <seealso marker="kernel:error_logger#format/2">error_logger:format/2</seealso>.</p>
- </desc>
- </func>
- <func>
- <name>Module:code_change(OldVsn, StateName, StateData, Extra) -> {ok, NextStateName, NewStateData}</name>
- <fsummary>Update the internal state data during upgrade/downgrade.</fsummary>
- <type>
- <v>OldVsn = Vsn | {down, Vsn}</v>
- <v>&nbsp;&nbsp;Vsn = term()</v>
- <v>StateName = NextStateName = atom()</v>
- <v>StateData = NewStateData = term()</v>
- <v>Extra = term()</v>
- </type>
- <desc>
- <p>This function is called by a gen_fsm when it should update
- its internal state data during a release upgrade/downgrade,
- i.e. when the instruction <c>{update,Module,Change,...}</c>
- where <c>Change={advanced,Extra}</c> is given in
- the <c>appup</c> file. See
- <seealso marker="doc/design_principles:release_handling#instr">OTP Design Principles</seealso>.</p>
- <p>In the case of an upgrade, <c>OldVsn</c> is <c>Vsn</c>, and
- in the case of a downgrade, <c>OldVsn</c> is
- <c>{down,Vsn}</c>. <c>Vsn</c> is defined by the <c>vsn</c>
- attribute(s) of the old version of the callback module
- <c>Module</c>. If no such attribute is defined, the version is
- the checksum of the BEAM file.</p>
- <p><c>StateName</c> is the current state name and
- <c>StateData</c> the internal state data of the gen_fsm.</p>
- <p><c>Extra</c> is passed as-is from the <c>{advanced,Extra}</c>
- part of the update instruction.</p>
- <p>The function should return the new current state name and
- updated internal data.</p>
- </desc>
- </func>
- <func>
- <name>Module:format_status(Opt, [PDict, StateData]) -> Status</name>
- <fsummary>Optional function for providing a term describing the
- current gen_fsm status.</fsummary>
- <type>
- <v>Opt = normal | terminate</v>
- <v>PDict = [{Key, Value}]</v>
- <v>StateData = term()</v>
- <v>Status = term()</v>
- </type>
- <desc>
- <note>
- <p>This callback is optional, so callback modules need not
- export it. The gen_fsm module provides a default
- implementation of this function that returns the callback
- module state data.</p>
- </note>
- <p>This function is called by a gen_fsm process when:</p>
- <list type="bulleted">
- <item>One
- of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
- is invoked to get the gen_fsm status. <c>Opt</c> is set to
- the atom <c>normal</c> for this case.</item>
- <item>The gen_fsm terminates abnormally and logs an
- error. <c>Opt</c> is set to the atom <c>terminate</c> for
- this case.</item>
- </list>
- <p>This function is useful for customising the form and
- appearance of the gen_fsm status for these cases. A callback
- module wishing to customise the <c>sys:get_status/1,2</c>
- return value as well as how its status appears in
- termination error logs exports an instance
- of <c>format_status/2</c> that returns a term describing the
- current status of the gen_fsm.</p>
- <p><c>PDict</c> is the current value of the gen_fsm's
- process dictionary.</p>
- <p><c>StateData</c> is the internal state data of the
- gen_fsm.</p>
- <p>The function should return <c>Status</c>, a term that
- customises the details of the current state and status of
- the gen_fsm. There are no restrictions on the
- form <c>Status</c> can take, but for
- the <c>sys:get_status/1,2</c> case (when <c>Opt</c>
- is <c>normal</c>), the recommended form for
- the <c>Status</c> value is <c>[{data, [{"StateData",
- Term}]}]</c> where <c>Term</c> provides relevant details of
- the gen_fsm state data. Following this recommendation isn't
- required, but doing so will make the callback module status
- consistent with the rest of the <c>sys:get_status/1,2</c>
- return value.</p>
- <p>One use for this function is to return compact alternative
- state data representations to avoid having large state terms
- printed in logfiles.</p>
- </desc>
- </func>
- </funcs>
+-ifdef(BEFORE_REWRITE).
+open(timeout, State) ->
+ do_lock(),
+ {next_state, locked, State};
+open({button,_}, Data) ->
+ {next_state, locked, Data}.
+-else.
+open(timeout, _, Data) ->
+ do_lock(),
+ {next_state, locked, Data};
+open(cast, {button,_}, Data) ->
+ {next_state, locked, Data};
+open({call, From}, Msg, Data) ->
+ handle_call(From, Msg, Data);
+open(info, Msg, Data) ->
+ handle_info(Msg, open, Data).
+%% Arity differs
+%% All state events are dispatched to handle_call and handle_info help
+%% functions. If you want to handle a call or cast event specifically
+%% for this state you would add a special clause for it above.
+-endif.
- <section>
- <title>SEE ALSO</title>
- <p><seealso marker="gen_event">gen_event(3)</seealso>,
- <seealso marker="gen_server">gen_server(3)</seealso>,
- <seealso marker="supervisor">supervisor(3)</seealso>,
- <seealso marker="proc_lib">proc_lib(3)</seealso>,
- <seealso marker="sys">sys(3)</seealso></p>
+-ifdef(BEFORE_REWRITE).
+handle_sync_event(stop, _From, _StateName, Data) ->
+ {stop, normal, ok, Data}.
+
+handle_event(Event, StateName, Data) ->
+ {stop, {shutdown, {unexpected, Event, StateName}}, Data}.
+
+handle_info(Info, StateName, Data) ->
+ {stop, {shutdown, {unexpected, Info, StateName}}, StateName, Data}.
+-else.
+-endif.
+
+terminate(_Reason, State, _Data) ->
+ State =/= locked andalso do_lock(),
+ ok.
+code_change(_Vsn, State, Data, _Extra) ->
+ {ok, State, Data}.
+
+%% Internal functions
+-ifdef(BEFORE_REWRITE).
+-else.
+handle_call(From, stop, Data) ->
+ {stop_and_reply, normal, {reply, From, ok}, Data}.
+
+handle_info(Info, StateName, Data) ->
+ {stop, {shutdown, {unexpected, Info, StateName}}, StateName, Data}.
+%% These are internal functions for handling all state events
+%% and not behaviour callbacks as in gen_fsm
+-endif.
+
+analyze_lock(Digit, #{code := Code, remaining := Remaining} = Data) ->
+ case Remaining of
+ [Digit] ->
+ do_unlock(),
+ {open, Data#{remaining := Code}};
+ [Digit|Rest] -> % Incomplete
+ {locked, Data#{remaining := Rest}};
+ _Wrong ->
+ {locked, Data#{remaining := Code}}
+ end.
+
+do_lock() ->
+ io:format("Lock~n", []).
+do_unlock() ->
+ io:format("Unlock~n", []).
+ </code>
</section>
</erlref>