From 759548838fa8b27eaa574233c9897d9578540a5a Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Mon, 22 Feb 2016 14:55:52 +0100 Subject: Make callback_option() mandatory --- lib/stdlib/doc/src/gen_statem.xml | 80 ++++++++++----------- lib/stdlib/src/gen_statem.erl | 131 +++++++++++++++++------------------ lib/stdlib/test/gen_statem_SUITE.erl | 53 ++++++++------ 3 files changed, 131 insertions(+), 133 deletions(-) (limited to 'lib') 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 specification to use when starting - agen_statem server. + a gen_statem server. See start_link/3 and @@ -347,17 +347,6 @@ erlang:'!' -----> Module:StateName/5

- - - -

Option that only is valid when initializing - the gen_statem that is it can be returned from - Module:init/1 - or given to - enter_loop/5,6. -

-
-
@@ -459,7 +448,7 @@ erlang:'!' -----> Module:StateName/5 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. + or inserted event counts just like a new in this respect. If Time =:= infinity no timer is started. If Time =:= 0 the timeout event is immediately enqueued as the newest received. @@ -468,7 +457,7 @@ erlang:'!' -----> Module:StateName/5 transition_action() cancel_timer. - This timeout is cancelled automatically by any event. + This timeout is cancelled automatically by any other event. @@ -478,9 +467,9 @@ erlang:'!' -----> Module:StateName/5

The state transition actions are executed in the containing list order. This matters - for next_event where the last one in the list - will become the next event to present - to the state functions. Regarding the other actions + for next_event 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 remove_event with EventPredicate and for reply_action() that the order may matter. @@ -490,7 +479,9 @@ erlang:'!' -----> Module:StateName/5 Reply to a calling client. next_event Insert the given EventType - and EventContent the next to process. + and EventContent 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 internal @@ -841,11 +832,11 @@ erlang:'!' -----> Module:StateName/5 - + Enter the gen_statem receive loop

The same as - enter_loop/6 + enter_loop/7 except that no server_name() @@ -854,12 +845,12 @@ erlang:'!' -----> Module:StateName/5 - + Enter the gen_statem receive loop

If Server_or_Ops is a list() the same as - enter_loop/6 + enter_loop/7 except that no server_name() @@ -867,7 +858,7 @@ erlang:'!' -----> Module:StateName/5 Ops = Server_or_Ops.

Otherwise the same as - enter_loop/6 + enter_loop/7 with Server = Server_or_Ops and Ops = []. @@ -875,7 +866,7 @@ erlang:'!' -----> Module:StateName/5 - + Enter the gen_statem receive loop

Makes an the calling process become a gen_statem. @@ -903,8 +894,8 @@ erlang:'!' -----> Module:StateName/5 server_name() name must have been registered accordingly before this function is called.

-

State, Data - and Ops +

CallbackMode, State, + Data and Ops have the same meanings as in the return value of Module:init/1. Also, the callback module Module @@ -938,15 +929,17 @@ erlang:'!' -----> Module:StateName/5 Initialize process and internal state Args = term() - Result = {ok,State,Data} -  | {ok,State,Data,Ops} + Result = {CallbackMode,State,Data} +  | {CallbackMode,State,Data,Ops}  | {stop,Reason} | ignore + CallbackMode = + callback_mode() + State = state() Data = data() Ops = - [transition_op() - | init_option()] + [transition_op()] Reason = term() @@ -962,21 +955,20 @@ erlang:'!' -----> Module:StateName/5

Args is the Args argument provided to the start function.

If the initialization is successful, the function should - return {ok,State,Data} or - {ok,State,Data,Ops}. - State is the state + return {CallbackMode,State,Data} or + {CallbackMode,State,Data,Ops}. + CallbackMode selects the + callback_mode(). of the gen_statem. + State is the state() + of the gen_statem and + Data the server data()

The Ops are executed when entering the first state just as for a state function.

-

This function allows an option to select the callback mode - of the gen_statem. See - init_option. - This option is not allowed from the state function(s). -

If something goes wrong during the initialization the function should return {stop,Reason} or ignore. See @@ -984,7 +976,7 @@ erlang:'!' -----> Module:StateName/5

This function may use throw, - to return its value. + to return Result.

@@ -1065,7 +1057,7 @@ erlang:'!' -----> Module:StateName/5

These functions may use throw, - to return its value. + to return Result.

@@ -1130,7 +1122,7 @@ erlang:'!' -----> Module:StateName/5

This function may use throw, - to return its value. + to return Ignored, which is ignored anyway.

@@ -1184,11 +1176,11 @@ erlang:'!' -----> Module:StateName/5

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

-

This function may use throw, - to return its value. + to return Result or Reason.

+ @@ -1277,7 +1269,7 @@ erlang:'!' -----> Module:StateName/5

This function may use throw, - to return its value. + to return Status.

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}); -- cgit v1.2.3