From 6ace96d3e5c9ac8ace3d8967bcafb3e6a081d9be Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 26 Oct 2015 11:52:17 +0100 Subject: New state machine --- lib/stdlib/doc/src/Makefile | 3 +- lib/stdlib/doc/src/gen_statem.xml | 1131 +++++++++++++++++++++++++++++++++++++ lib/stdlib/doc/src/ref_man.xml | 3 +- lib/stdlib/doc/src/specs.xml | 1 + 4 files changed, 1136 insertions(+), 2 deletions(-) create mode 100644 lib/stdlib/doc/src/gen_statem.xml (limited to 'lib/stdlib/doc/src') diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index 196c86748f..26602764a6 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2015. All Rights Reserved. +# Copyright Ericsson AB 1997-2016. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -68,6 +68,7 @@ XML_REF3_FILES = \ gen_event.xml \ gen_fsm.xml \ gen_server.xml \ + gen_statem.xml \ io.xml \ io_lib.xml \ lib.xml \ diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml new file mode 100644 index 0000000000..885021f61c --- /dev/null +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -0,0 +1,1131 @@ + + + + +
+ + 2016 + Ericsson AB. All Rights Reserved. + + + 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. + + + + gen_statem + + + + +
+ gen_statem + Generic State Machine Behaviour + +

A behaviour module for implementing a state machine, primarily + a finite state machine, but an infinite state space is possible. + 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 + + 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:

+
+gen_statem module            Callback module
+-----------------            ---------------
+gen_statem:start
+gen_statem:start_link -----> Module:init/1
+
+gen_statem:stop       -----> Module:terminate/2
+
+gen_statem:call
+gen_statem:cast
+erlang:send
+erlang:'!'            -----> Module:State/5
+                             Module:handle_event/5
+
+-                     -----> Module:terminate/3
+
+-                     -----> Module:code_change/3
+

Events are of different + 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 "state function" for a specific + state + in a gen_statem is the callback function that is called + for all events in this state. + An event can can be postponed causing it to be retried + after the state has changed, or more precisely; + after a state change all postponed events are retried + in the new state. +

+

The state machine + State + is normally an atom in which case the + state function + that will be called is + Module:State/5. + For a + State + that is not an atom the + state function + + Module:handle_event/5 + will be called. + If you use handle_event as a + state and later + decides to use non-atom states you will then have to + rewrite your code to stop using that state. +

+

When the using an atom-only + State + it becomes fairly obvious in the implementation code + which events are handled in which state + since there are different callback functions for different states. +

+

+ When using a non-atom State + all events are handled in the callback function + + Module:handle_event/5 + + so it may require more coding discipline to ensure what events + are handled in which state. Therefore it might be a wee bit + easier to accidentally postpone an event in two or more states + and not handling it in any of them causing a tight infinite + loop when the event bounces to be retried between the states. +

+

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

+

Note that a gen_statem does not trap exit signals automatically, + this must be explicitly initiated by the callback module. +

+

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

+

The gen_statem process can go into hibernation (see + + erlang:hibernate/3 + ) if a + state function or + Module:init/1 + specifies 'hibernate' in the returned + StateOps list. + This might be useful if the server is expected to be idle + for a long time. However use this feature with care + since hibernation implies at least two garbage collections + (when hibernating and shortly after waking up) and that is not + something you'd want to do between each event on a busy server. +

+
+ + + + + +

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

+
+
+ + + +

Server specification to use when addressing a gen_statem server. + See call/2 and + + server_name() + above. +

+

It can be:

+ + the pid(), + Name, + if the gen_statem is locally registered, + + {Name,Node}, + if the gen_statem is locally registered at another node, or + + {global,GlobalName}, + if the gen_statem is globally registered. + + {via,RegMod,ViaName}, + if 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}. + + +
+
+ + + +

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

+

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

+
+
+ + + +

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

+
+
+ + + +

Return value from the start functions for_example + start_link/3. +

+
+
+ + + + +

Client address to use when replying through for example the + state_op() + {reply,Client,Reply} to a client + that has called the gen_statem server using + call/2. +

+
+
+ + + +

If the gen_statem State is an atom(), the + state function is + Module:State/5. + If it is any other term() the + state function is + + Module:handle_event/5 + . After a state change (NewState =/= State) + all postponed events are retried. +

+
+
+ + + +

A term() in which the state machine implementation + should store any state data it needs. The difference between + this data and the + state() + itself is that a change in this data does not cause + postponed events to be retried. +

+
+
+ + + +

External events are of 3 different type: + {call,Client}, cast or info. + Calls (synchronous) and casts (asynchronous) + originate from the corresponding API functions. + For calls the event contain whom to reply to. + Type info originates from normal messages sent + to the gen_statem process. + It is also possible for the state machine + implementation to insert events to itself, + in particular of types + timeout and internal. +

+
+
+ + + +

A fun() of arity 2 that takes an event + and returns a boolean. + When used in {remove_event,RemoveEventPredicate} + from state_op(). + The event for which the predicate returns true will + be removed. +

+

+ The predicate may not use a throw exception + to return its result. +

+
+
+ + + +

Either a + + state_option() + of which the first occurence + in the containing list takes precedence, or a + + state_operation() + that are performed in order of + the containing list. +

+

These may be returned from the + state function + or from Module:init/1. +

+

The gen_statem enqueues postponed events and + not yet processed events in order of arrival, except for + an event that a callback function inserts with + {insert_event,EventType,EventContent} that is inserted + as the next event to process. +

+

When the state machine changes states all enqueued events + becomes not yet processed to be processed before the old + not yet processed events. In other words; the order of arrival + is retained. +

+

The processing order is:

+ + If the option retry is true + the current event is enqueued as postponed to be retried. + + If the state changes all postponed events + are transferred to not yet processed to be processed + before other not yet processed events. + + All operations are processed in order of appearance. + + The timeout option is processed if present. + So a state timer may be started or a timeout zero event + may be inserted as if just received. + + The (possibly new) + state function + is called with the next not yet processed event + if there is any, otherwise the gen_statem goes into receive + or hibernation (if the option hibernate is true) + to wait for the next message. In hibernation the next + non-system event awakens the gen_statem. + + +
+
+ + + + + retry + {retry,Retry} + If Retry =:= true + or plain retry postpone the current event + to be retried after a state change. + + hibernate + {hibernate,Hibernate} + If Hibernate =:= true + or plain hibernate hibernate the gen_statem by calling + + proc_lib:hibernate/3 + before receive to wait for a new event. + If there are not yet processed events the + hibernate operation is ignored as if an event + just arrived and awakened the gen_statem. + + + {timeout,Time,Msg} + + Generate an event of + type timeout + after Time milliseconds unless some other + event is received before that time. Note that a retried + event counts just like a new in this respect. + If Time =:= infinity or Time =:= 0 + no timer is started but for zero time the timeout + event is enqued as just received after all + other already received events. + Also note that it is not possible + to cancel this timeout using the + + state_operation() + cancel_timer. + This timeout is cancelled automatically by any event. + + + + + + + + + + {reply,Client,Reply} + + Reply to a client that called + call/2. + Client must be the term from the + + {call,Client} + argument to the + state function. + + {stop,Reason} + The gen_statem will call + + Module:terminate/3 + with Reason and terminate. + + + + {insert_event,EventType,EventContent} + + + Insert the given event as the next to process + before any other not yet processed events. + An event of type + + internal + should be used when you want to reliably distinguish + an event inserted this way from any external event. + + + + {remove_event,EventType,EventContent} + + + Remove the oldest queued event + that matches equal to the given event. + + + + {remove_event,EventPredicate} + + + Remove the oldest queued event for which + the EventPredicate returns true. + + {cancel_timer,TimerRef} + Uses TimerRef when calling + + erlang:cancel_timer/2 + to cancel a timer, cleans the gen_statem's + message queue from any late timeout message from + the timer, and removes any late timeout message + from the queued events using + {remove_event,EventPredicate} above. + This is a convenience function that saves quite some + lines of code and testing time over doing it from + the primitives mentioned above. + + {demonitor,MonitorRef} + Like {cancel_timer,_} above but for + + demonitor/2 + . + + {unlink,Id} + Like {cancel_timer,_} above but for + + unlink/1 + . + + + + +
+ + + + + + + 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 + Options, 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 Options, debugging through + sys is activated. +

+

If the option {spawn_opt,SOpts} is present in + Options, SOpts will be passed + as option list to the spawn_opt BIF + which is used to + spawn + the gen_statem. +

+ +

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

Makes a synchronous call to the gen_statem + + ServerRef + by sending a request + and waiting until its reply arrives. + The gen_statem will call the + state function with + event_type() + {call,Client} and event content + Request. +

+

A Reply is generated when a + state function + returns with + {reply,Client,Reply} as one + state_op(), + and that Reply becomes the return value + of this function. +

+

Timeout is an integer greater than zero + 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 + 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 + less efficient than using + Timeout =:= infinity. +

+ +

+

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

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

Sends an asynchronous event to the gen_statem + + ServerRef + and returns ok immediately, + ignoring if the destination node or gen_statem does not exist. + The gen_statem will call the + state function with + event_type() + cast and event content + Msg. +

+
+
+ + + + Send a reply to a client + +

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

+

Client must be the term from the + + {call,Client} + argument to the + state function. +

+ +

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

+
+
+
+ + + + Enter the gen_statem receive loop + +

The same as + enter_loop/6 + except that no + + server_name() + must have been registered. +

+
+
+ + + Enter the gen_statem receive loop + +

If Server_or_StateOps is a list() + the same as + enter_loop/6 + except that no + + server_name() + must have been registered and + StateOps = Server_or_StateOps. +

+

Otherwise the same as + enter_loop/6 + with + Server = Server_or_StateOps and + StateOps = []. +

+
+
+ + + 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 a gen_statem server. The process + must have been started using one of the start + functions in + 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. +

+

Module, Options and + Server have the same meanings + as when calling + gen_statem:start[_link]/3,4. + However, the + + server_name() + name must have been registered accordingly + before this function is called.

+

State and StateData + have the same meanings as in the return value of + 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 + start function, or if it is not registered + 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 = {ok,State,StateData} +  | {ok,State,StateData,StateOps} +  | {stop,Reason} | ignore + State = state() + StateData = state_data() + StateOps = [state_op()] + Reason = term() + + + +

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

+

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

+

If the initialization is successful, the function should + return {ok,State,StateData} or + {ok,State,StateData,StateOps}. + State is the state + of the gen_statem. +

+

The StateOps + are executed before entering the first + state just as for a + state function. +

+

If something goes wrong during the initialization + the function should return {stop,Reason} + or ignore. See + gen_statem:start_link/3,4. +

+
+
+ + + Module:handle_event(EventType, EventContent, + PrevState, State, StateData) -> Result + + Module:State(EventType, EventContent, + PrevState, State, StateData) -> Result + + Handle an event + + EventType = + event_type() + + EventContent = term() + Result = {NewState,NewStateData,StateOps} +   | {NewState,NewStateData} +   The same as {NewState,NewStateData,[]} +   | {NewStateData} +   The same as {State,NewStateData,[retry]} +   | {} +   The same as {State,StateData,[]} +   | StateOps +   The same as {State,StateData,StateOps} + + PrevState = State = NewState = + state() + + StateData = NewStateData = + state_data() + + StateOps = + [state_op()] + + + +

Whenever a gen_statem receives an event from + gen_statem:call/2, + gen_statem:cast/2 or + as a normal process message this function is called. + If the EventType is + {call,Client} + the client is waiting for a reply. The reply can be sent + from this or from any other + state function + by returning with {reply,Client,Reply} in + StateOps + or by calling + + gen_statem:reply(Client, Reply) + . +

+

State + is the internal state of the gen_statem which + when State is an atom() + is the same as this function's name, so it is seldom useful, + except for example when comparing with PrevState + that is the gen_statem's previous state, or in + + Module:handle_event/5 + since that function is common for all states + that are not an atom(). +

+

If this function returns with + NewState =/= State + all postponed events will be retried in the new state. +

+

See state_op() + for the operations that can be done by gen_statem + after returning from this function. +

+
+
+ + + Module:terminate(Reason, State, StateData) + Clean up before termination + + Reason = normal | shutdown | {shutdown,term()} | term() + State = state() + StateData = + + state_data() + + + + +

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 + StateOps, + Reason will have the value specified in that tuple. + If it is due to 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 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. +

+

Otherwise, the gen_statem will be immediately terminated. +

+

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 + . +

+
+
+ + + Module:code_change(OldVsn, OldState, OldStateData, Extra) -> + Result + + Update the internal state during upgrade/downgrade + + OldVsn = Vsn | {down,Vsn} +   Vsn = term() + OldState = NewState = term() + Extra = term() + Result = {ok,{NewState,NewStateData}} | Reason + OldState = NewState = + state() + + OldStateData = NewStateData = + state_data() + + Reason = term() + + +

This function is called by a gen_statem when it should + update its internal state during a release upgrade/downgrade, + i.e. when the instruction {update,Module,Change,...} + where Change={advanced,Extra} is given in + the appup file. 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. +

+

OldState and OldStateData is the internal state + of the gen_statem. +

+

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 + {ok,{NewState,NewStateData}} tuple. +

+

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

+
+
+ + + Module:format_status(Opt, [PDict,State,StateData]) -> + Status + + Optional function for providing a term describing the + current gen_statem status + + Opt = normal | terminate + PDict = [{Key, Value}] + State = + state() + + StateData = + state_data() + + Key = term() + Value = term() + Status = term() + + + +

This callback is optional, so callback modules need not + export it. The gen_statem module provides a default + implementation of this function that returns the callback + module state. +

+
+

This function is called by a gen_statem process when:

+ + 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. + + 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 + appearance of the gen_statem status for these cases. A + callback module wishing to customise the + + sys:get_status/1,2 + return value as well as how + its status appears in termination error logs exports an + instance of format_status/2 that returns a term + describing the current status of the gen_statem. +

+

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

+

State + is the internal state of the gen_statem. +

+

The function should return Status, a term that + customises 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 + 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 + 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. +

+
+
+ +
+ +
+ SEE ALSO +

gen_event, + gen_fsm, + gen_server, + supervisor, + proc_lib, + sys

+
+
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml index 82ad78e675..404873ea32 100644 --- a/lib/stdlib/doc/src/ref_man.xml +++ b/lib/stdlib/doc/src/ref_man.xml @@ -4,7 +4,7 @@
- 19962015 + 19962016 Ericsson AB. All Rights Reserved. @@ -66,6 +66,7 @@ + diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml index 0418bf7b22..45b207b13d 100644 --- a/lib/stdlib/doc/src/specs.xml +++ b/lib/stdlib/doc/src/specs.xml @@ -30,6 +30,7 @@ + -- cgit v1.2.3