aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml80
-rw-r--r--lib/stdlib/src/gen_statem.erl131
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl53
3 files changed, 131 insertions, 133 deletions
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index 8462f5ff5f..bd210a0d22 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -190,7 +190,7 @@ erlang:'!' -----> Module:StateName/5
<name name="server_name" />
<desc>
<p>Name specification to use when starting
- a<c>gen_statem</c> server.
+ a <c>gen_statem</c> server.
See <seealso marker="#start_link/3">
<c>start_link/3</c>
</seealso> and
@@ -348,17 +348,6 @@ erlang:'!' -----> Module:StateName/5
</desc>
</datatype>
<datatype>
- <name name="init_option" />
- <desc>
- <p>Option that only is valid when initializing
- the <c>gen_statem</c> that is it can be returned from
- <seealso marker="#Module:init/1">Module:init/1</seealso>
- or given to
- <seealso marker="#enter_loop/5">enter_loop/5,6</seealso>.
- </p>
- </desc>
- </datatype>
- <datatype>
<name name="callback_mode" />
<desc>
<taglist>
@@ -459,7 +448,7 @@ erlang:'!' -----> Module:StateName/5
<seealso marker="#type-event_type">type <c>timeout</c></seealso>
after <c><anno>Time</anno></c> milliseconds unless some other
event is received before that time. Note that a retried
- event counts just like a new in this respect.
+ or inserted event counts just like a new in this respect.
If <c>Time =:= infinity</c> no timer is started.
If <c>Time =:= 0</c> the timeout event
is immediately enqueued as the newest received.
@@ -468,7 +457,7 @@ erlang:'!' -----> Module:StateName/5
<seealso marker="#type-transition_action">
<c>transition_action()</c>
</seealso> <c>cancel_timer</c>.
- This timeout is cancelled automatically by any event.
+ This timeout is cancelled automatically by any other event.
</item>
</taglist>
</desc>
@@ -478,9 +467,9 @@ erlang:'!' -----> Module:StateName/5
<desc>
<p>The state transition actions are executed
in the containing list order. This matters
- for <c>next_event</c> where the last one in the list
- will become the next event to present
- to the state functions. Regarding the other actions
+ for <c>next_event</c> where the last such in the list
+ will become the next event to process by
+ the current state function. Regarding the other actions
it is only for <c>remove_event</c> with
<c><anno>EventPredicate</anno></c>
and for <c>reply_action()</c> that the order may matter.
@@ -490,7 +479,9 @@ erlang:'!' -----> Module:StateName/5
<item>Reply to a calling client.</item>
<tag><c>next_event</c></tag>
<item>Insert the given <c><anno>EventType</anno></c>
- and <c><anno>EventContent</anno></c> the next to process.
+ and <c><anno>EventContent</anno></c> as the next to process.
+ This will bypass any events in the process mailbox as well
+ as any other queued events.
An event of type
<seealso marker="#type-event_type">
<c>internal</c>
@@ -841,11 +832,11 @@ erlang:'!' -----> Module:StateName/5
</func>
<func>
- <name name="enter_loop" arity="4" />
+ <name name="enter_loop" arity="5" />
<fsummary>Enter the <c>gen_statem</c> receive loop</fsummary>
<desc>
<p>The same as
- <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
except that no
<seealso marker="#type-server_name">
<c>server_name()</c>
@@ -854,12 +845,12 @@ erlang:'!' -----> Module:StateName/5
</desc>
</func>
<func>
- <name name="enter_loop" arity="5" />
+ <name name="enter_loop" arity="6" />
<fsummary>Enter the <c>gen_statem</c> receive loop</fsummary>
<desc>
<p>If <c><anno>Server_or_Ops</anno></c> is a <c>list()</c>
the same as
- <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
except that no
<seealso marker="#type-server_name">
<c>server_name()</c>
@@ -867,7 +858,7 @@ erlang:'!' -----> Module:StateName/5
<c>Ops = <anno>Server_or_Ops</anno></c>.
</p>
<p>Otherwise the same as
- <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>
+ <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso>
with
<c>Server = <anno>Server_or_Ops</anno></c> and
<c>Ops = []</c>.
@@ -875,7 +866,7 @@ erlang:'!' -----> Module:StateName/5
</desc>
</func>
<func>
- <name name="enter_loop" arity="6" />
+ <name name="enter_loop" arity="7" />
<fsummary>Enter the <c>gen_statem</c> receive loop</fsummary>
<desc>
<p>Makes an the calling process become a <c>gen_statem</c>.
@@ -903,8 +894,8 @@ erlang:'!' -----> Module:StateName/5
<c>server_name()</c>
</seealso> name must have been registered accordingly
<em>before</em> this function is called.</p>
- <p><c><anno>State</anno></c>, <c><anno>Data</anno></c>
- and <c><anno>Ops</anno></c>
+ <p><c><anno>CallbackMode</anno></c>, <c><anno>State</anno></c>,
+ <c><anno>Data</anno></c> and <c><anno>Ops</anno></c>
have the same meanings as in the return value of
<seealso marker="#Module:init/1">Module:init/1</seealso>.
Also, the callback module <c><anno>Module</anno></c>
@@ -938,15 +929,17 @@ erlang:'!' -----> Module:StateName/5
<fsummary>Initialize process and internal state</fsummary>
<type>
<v>Args = term()</v>
- <v>Result = {ok,State,Data}</v>
- <v>&nbsp;| {ok,State,Data,Ops}</v>
+ <v>Result = {CallbackMode,State,Data}</v>
+ <v>&nbsp;| {CallbackMode,State,Data,Ops}</v>
<v>&nbsp;| {stop,Reason} | ignore</v>
+ <v>CallbackMode =
+ <seealso marker="#type-callback_mode">callback_mode()</seealso>
+ </v>
<v>State = <seealso marker="#type-state">state()</seealso></v>
<v>Data = <seealso marker="#type-data">data()</seealso>
</v>
<v>Ops =
- [<seealso marker="#type-transition_op">transition_op()</seealso>
- | <seealso marker="#type-init_option">init_option()</seealso>]
+ [<seealso marker="#type-transition_op">transition_op()</seealso>]
</v>
<v>Reason = term()</v>
</type>
@@ -962,21 +955,20 @@ erlang:'!' -----> Module:StateName/5
<p><c>Args</c> is the <c>Args</c> argument provided to the start
function.</p>
<p>If the initialization is successful, the function should
- return <c>{ok,State,Data}</c> or
- <c>{ok,State,Data,Ops}</c>.
- <c>State</c> is the <seealso marker="#type-state">state</seealso>
+ return <c>{CallbackMode,State,Data}</c> or
+ <c>{CallbackMode,State,Data,Ops}</c>.
+ <c>CallbackMode</c> selects the
+ <seealso marker="#type-callback_mode">callback_mode()</seealso>.
of the <c>gen_statem</c>.
+ <c>State</c> is the <seealso marker="#type-state">state()</seealso>
+ of the <c>gen_statem</c> and
+ <c>Data</c> the server <seealso marker="#type-data">data()</seealso>
</p>
<p>The <seealso marker="#type-transition_op"><c>Ops</c></seealso>
are executed when entering the first
<seealso marker="#type-state">state</seealso> just as for a
<seealso marker="#state_function">state function</seealso>.
</p>
- <p>This function allows an option to select the callback mode
- of the <c>gen_statem</c>. See
- <seealso marker="#type-init_option">init_option</seealso>.
- This option is not allowed from the state function(s).
- </p>
<p>If something goes wrong during the initialization
the function should return <c>{stop,Reason}</c>
or <c>ignore</c>. See
@@ -984,7 +976,7 @@ erlang:'!' -----> Module:StateName/5
</p>
<p>This function may use
<seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>,
- to return its value.
+ to return <c>Result</c>.
</p>
</desc>
</func>
@@ -1065,7 +1057,7 @@ erlang:'!' -----> Module:StateName/5
</p>
<p>These functions may use
<seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>,
- to return its value.
+ to return <c>Result</c>.
</p>
</desc>
</func>
@@ -1130,7 +1122,7 @@ erlang:'!' -----> Module:StateName/5
</p>
<p>This function may use
<seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>,
- to return its value.
+ to return <c>Ignored</c>, which is ignored anyway.
</p>
</desc>
</func>
@@ -1184,11 +1176,11 @@ erlang:'!' -----> Module:StateName/5
</p>
<p>If the function returns <c>Reason</c>, the ongoing
upgrade will fail and roll back to the old release.</p>
- </desc>
<p>This function may use
<seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>,
- to return its value.
+ to return <c>Result</c> or <c>Reason</c>.
</p>
+ </desc>
</func>
<func>
@@ -1277,7 +1269,7 @@ erlang:'!' -----> Module:StateName/5
</p>
<p>This function may use
<seealso marker="erts:erlang#throw/1"><c>throw</c></seealso>,
- to return its value.
+ to return <c>Status</c>.
</p>
</desc>
</func>
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index b580eaab97..32799f5afc 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -24,7 +24,7 @@
[start/3,start/4,start_link/3,start_link/4,
stop/1,stop/3,
cast/2,call/2,call/3,
- enter_loop/4,enter_loop/5,enter_loop/6,
+ enter_loop/5,enter_loop/6,enter_loop/7,
reply/1,reply/2]).
%% gen callbacks
@@ -63,8 +63,6 @@
'info' | 'timeout' | 'internal'.
-type event_predicate() :: % Return true for the event in question
fun((event_type(), term()) -> boolean()).
--type init_option() ::
- {'callback_mode', callback_mode()}.
-type callback_mode() :: 'state_functions' | 'handle_event_function'.
-type transition_op() ::
%% First NewState and NewData are set,
@@ -136,12 +134,12 @@
%% an {ok, ...} tuple. Thereafter the state callbacks are called
%% for all events to this server.
-callback init(Args :: term()) ->
- {'ok', state(), data()} |
- {'ok', state(), data(), [transition_op()|init_option()]} |
+ {callback_mode(), state(), data()} |
+ {callback_mode(), state(), data(), [transition_op()]} |
'ignore' |
{'stop', Reason :: term()}.
-%% Example callback for callback_mode =:= state_functions
+%% Example state callback for callback_mode() =:= state_functions
%% state name 'state_name'.
%%
%% In this mode all states has to be type state_name() i.e atom().
@@ -156,7 +154,7 @@
Data :: data()) ->
state_callback_result().
%%
-%% Callback for callback_mode =:= handle_event_function.
+%% State callback for callback_mode() =:= handle_event_function.
%%
%% Note that state callbacks and only state callbacks have arity 5
%% and that is intended.
@@ -199,7 +197,8 @@
StatusOption :: 'normal' | 'terminate'.
-optional_callbacks(
- [format_status/2, % Has got a default implementation
+ [init/1, % One may use enter_loop/5,6,7 instead
+ format_status/2, % Has got a default implementation
%%
state_name/5, % Example for callback_mode =:= state_functions:
%% there has to be a StateName/5 callback function for every StateName.
@@ -207,6 +206,16 @@
handle_event/5]). % For callback_mode =:= handle_event_function
%% Type validation functions
+callback_mode(CallbackMode) ->
+ case CallbackMode of
+ state_functions ->
+ true;
+ handle_event_function ->
+ true;
+ _ ->
+ false
+ end.
+%%
client({Pid,Tag}) when is_pid(Pid), is_reference(Tag) ->
true;
client(_) ->
@@ -413,38 +422,41 @@ reply({To,Tag}, Reply) ->
%% the same arguments as you would have returned from init/1
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
State :: state(), Data :: data()) ->
no_return().
-enter_loop(Module, Opts, State, Data) ->
- enter_loop(Module, Opts, State, Data, self()).
+enter_loop(Module, Opts, CallbackMode, State, Data) ->
+ enter_loop(Module, Opts, CallbackMode, State, Data, self()).
%%
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
State :: state(), Data :: data(),
Server_or_Ops ::
- server_name() | pid() | [transition_op()|init_option()]) ->
+ server_name() | pid() | [transition_op()]) ->
no_return().
-enter_loop(Module, Opts, State, Data, Server_or_Ops) ->
+enter_loop(Module, Opts, CallbackMode, State, Data, Server_or_Ops) ->
if
is_list(Server_or_Ops) ->
enter_loop(
- Module, Opts, State, Data,
+ Module, Opts, CallbackMode, State, Data,
self(), Server_or_Ops);
true ->
enter_loop(
- Module, Opts, State, Data,
+ Module, Opts, CallbackMode, State, Data,
Server_or_Ops, [])
end.
%%
-spec enter_loop(
Module :: module(), Opts :: [debug_opt()],
+ CallbackMode :: callback_mode(),
State :: state(), Data :: data(),
Server :: server_name() | pid(),
- Ops :: [transition_op()|init_option()]) ->
+ Ops :: [transition_op()]) ->
no_return().
-enter_loop(Module, Opts, State, Data, Server, Ops) ->
+enter_loop(Module, Opts, CallbackMode, State, Data, Server, Ops) ->
Parent = gen:get_parent(),
- enter(Module, Opts, State, Data, Server, Ops, Parent).
+ enter(Module, Opts, CallbackMode, State, Data, Server, Ops, Parent).
%%---------------------------------------------------------------------------
%% API helpers
@@ -465,37 +477,40 @@ do_send(Proc, Msg) ->
ok
end.
-%% Here init_it and all enter_loop functions converge
-enter(Module, Opts, State, Data, Server, InitOps, Parent) ->
- Name = gen:get_proc_name(Server),
- Debug = gen:debug_options(Name, Opts),
- PrevState = undefined,
- S = #{
- callback_mode => state_functions,
- module => Module,
- name => Name,
- prev_state => PrevState,
- state => PrevState, % Will be discarded by loop_event_transition_ops
- data => Data,
- timer => undefined,
- postponed => [],
- hibernate => false},
- case collect_init_options(InitOps) of
- {CallbackMode,Ops} ->
+%% Here init_it/6 and enter_loop/5,6,7 functions converge
+enter(Module, Opts, CallbackMode, State, Data, Server, Ops, Parent)
+ when is_atom(Module), is_pid(Parent) ->
+ case callback_mode(CallbackMode) of
+ true ->
+ Name = gen:get_proc_name(Server),
+ Debug = gen:debug_options(Name, Opts),
+ PrevState = undefined,
+ S = #{
+ callback_mode => CallbackMode,
+ module => Module,
+ name => Name,
+ prev_state => PrevState,
+ state => PrevState, % Discarded by loop_event_transition_ops
+ data => Data,
+ timer => undefined,
+ postponed => [],
+ hibernate => false},
loop_event_transition_ops(
- Parent, Debug,
- S#{callback_mode := CallbackMode},
- [],
- {event,undefined}, % Will be discarded by {postpone,false}
+ Parent, Debug, S, [],
+ {event,undefined}, % Discarded due to {postpone,false}
PrevState, State, Data,
Ops++[{postpone,false}]);
- [Reason] ->
- ?TERMINATE(Reason, Debug, S, [])
+ false ->
+ erlang:error(
+ badarg,
+ [Module,Opts,CallbackMode,State,Data,Server,Ops,Parent])
end.
%%%==========================================================================
%%% gen callbacks
+init_it(Starter, self, ServerRef, Module, Args, Opts) ->
+ init_it(Starter, self(), ServerRef, Module, Args, Opts);
init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
try Module:init(Args) of
Result ->
@@ -514,12 +529,16 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
case Result of
- {ok,State,Data} ->
+ {CallbackMode,State,Data} ->
proc_lib:init_ack(Starter, {ok,self()}),
- enter(Module, Opts, State, Data, ServerRef, [], Parent);
- {ok,State,Data,Ops} ->
+ enter(
+ Module, Opts, CallbackMode, State, Data,
+ ServerRef, [], Parent);
+ {CallbackMode,State,Data,Ops} ->
proc_lib:init_ack(Starter, {ok,self()}),
- enter(Module, Opts, State, Data, ServerRef, Ops, Parent);
+ enter(
+ Module, Opts, CallbackMode, State, Data,
+ ServerRef, Ops, Parent);
{stop,Reason} ->
gen:unregister_name(ServerRef),
proc_lib:init_ack(Starter, {error,Reason}),
@@ -881,30 +900,6 @@ loop_event_transition_ops(
%%---------------------------------------------------------------------------
%% Server helpers
-collect_init_options(InitOps) ->
- if
- is_list(InitOps) ->
- collect_init_options(InitOps, state_functions, []);
- true ->
- collect_init_options([InitOps], state_functions, [])
- end.
-%% Keep the last of each kind
-collect_init_options([], CallbackMode, Ops) ->
- {CallbackMode,lists:reverse(Ops)};
-collect_init_options(
- [InitOp|InitOps] = AllInitOps, CallbackMode, Ops) ->
- case InitOp of
- {callback_mode,Mode}
- when Mode =:= state_functions;
- Mode =:= handle_event_function ->
- collect_init_options(InitOps, Mode, Ops);
- {callback_mode,_} ->
- [{bad_init_ops,AllInitOps}];
- _ -> % Collect others as Ops
- collect_init_options(
- InitOps, CallbackMode, [InitOp|Ops])
- end.
-
collect_transition_options(Ops) ->
if
is_list(Ops) ->
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 65a8d35645..a8b4d16f23 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -36,7 +36,8 @@ all() ->
{group, abnormal},
{group, abnormal_handle_event},
shutdown,
- {group, sys}, hibernate, enter_loop].
+ {group, sys},
+ hibernate, enter_loop].
groups() ->
[{start, [],
@@ -73,7 +74,7 @@ init_per_group(GroupName, Config)
GroupName =:= stop_handle_event;
GroupName =:= abnormal_handle_event;
GroupName =:= sys_handle_event ->
- [{init_ops,[{callback_mode,handle_event_function}]}|Config];
+ [{callback_mode,handle_event_function}|Config];
init_per_group(_GroupName, Config) ->
Config.
@@ -86,6 +87,8 @@ init_per_testcase(_CaseName, Config) ->
%%% dbg:tracer(),
%%% dbg:p(all, c),
%%% dbg:tpl(gen_statem, cx),
+%%% dbg:tpl(proc_lib, cx),
+%%% dbg:tpl(gen, cx),
%%% dbg:tpl(sys, cx),
[{watchdog, Dog} | Config].
@@ -901,8 +904,11 @@ enter_loop(Config) when is_list(Config) ->
end,
%% Process not started using proc_lib
+ CallbackMode = state_functions,
Pid4 =
- spawn_link(gen_statem, enter_loop, [?MODULE,[],state0,[]]),
+ spawn_link(
+ gen_statem, enter_loop,
+ [?MODULE,[],CallbackMode,state0,[]]),
receive
{'EXIT',Pid4,process_was_not_started_by_proc_lib} ->
ok
@@ -976,16 +982,21 @@ enter_loop(Reg1, Reg2) ->
anon -> ignore
end,
proc_lib:init_ack({ok, self()}),
+ CallbackMode = state_functions,
case Reg2 of
local ->
- gen_statem:enter_loop(?MODULE, [], state0, [], {local,armitage});
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [], {local,armitage});
global ->
- gen_statem:enter_loop(?MODULE, [], state0, [], {global,armitage});
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [], {global,armitage});
via ->
- gen_statem:enter_loop(?MODULE, [], state0, [],
- {via, dummy_via, armitage});
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [],
+ {via, dummy_via, armitage});
anon ->
- gen_statem:enter_loop(?MODULE, [], state0, [])
+ gen_statem:enter_loop(
+ ?MODULE, [], CallbackMode, state0, [])
end.
%%
@@ -1098,9 +1109,9 @@ verify_empty_msgq() ->
ok.
start_arg(Config, Arg) ->
- case lists:keyfind(init_ops, 1, Config) of
- {_,Ops} ->
- {init_ops,Arg,Ops};
+ case lists:keyfind(callback_mode, 1, Config) of
+ {_,CallbackMode} ->
+ {callback_mode,CallbackMode,Arg};
false ->
Arg
end.
@@ -1119,24 +1130,24 @@ init(stop_shutdown) ->
{stop,shutdown};
init(sleep) ->
?t:sleep(1000),
- {ok,idle,data};
+ {state_functions,idle,data};
init(hiber) ->
- {ok,hiber_idle,[]};
+ {state_functions,hiber_idle,[]};
init(hiber_now) ->
- {ok,hiber_idle,[],[hibernate]};
+ {state_functions,hiber_idle,[],[hibernate]};
init({data, Data}) ->
- {ok,idle,Data};
-init({init_ops,Arg,InitOps}) ->
+ {state_functions,idle,Data};
+init({callback_mode,CallbackMode,Arg}) ->
case init(Arg) of
- {ok,State,Data,Ops} ->
- {ok,State,Data,InitOps++Ops};
- {ok,State,Data} ->
- {ok,State,Data,InitOps};
+ {_,State,Data,Ops} ->
+ {CallbackMode,State,Data,Ops};
+ {_,State,Data} ->
+ {CallbackMode,State,Data};
Other ->
Other
end;
init([]) ->
- {ok,idle,data}.
+ {state_functions,idle,data}.
terminate(_, _State, crash_terminate) ->
exit({crash,terminate});