From 82f34a7a9de85b4afc0dac4c9c426939264c5039 Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Thu, 18 Feb 2016 16:06:57 +0100 Subject: Write some convenience helpers --- lib/stdlib/doc/src/gen_statem.xml | 23 +++++++++++++++++++++ lib/stdlib/src/gen_statem.erl | 39 +++++++++++++++++++++++++++++------- lib/stdlib/test/gen_statem_SUITE.erl | 24 +++++++++++----------- 3 files changed, 67 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index aeab09615c..5fbedb12f8 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -534,6 +534,13 @@ erlang:'!' -----> Module:StateName/5 + + {stop,Reason} + + The same as + {stop,Reason,[],StateData} + but keeps the old StateData. + {stop,Reason,NewStateData} @@ -551,6 +558,19 @@ erlang:'!' -----> Module:StateName/5 Module:terminate/3 with Reason. + + + {next_state,StateOps} + + + The same as + + {next_state,State,StateData,StateOps} + but keeps the old State and StateData. + Believe it or not, but this one has actually + been proven useful to throw/1 from deep down + in the state logic. + {next_state,NewState,NewStateData} @@ -805,6 +825,7 @@ erlang:'!' -----> Module:StateName/5 + Send a reply to a client @@ -820,6 +841,8 @@ erlang:'!' -----> Module:StateName/5 {call,Client} argument to the state function. + Client and Reply + an also be specified using ReplyOperation.

A reply sent with this function will not be visible diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index 32174687cf..486f61b1ed 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -25,7 +25,7 @@ stop/1,stop/3, cast/2,call/2,call/3, enter_loop/4,enter_loop/5,enter_loop/6, - reply/2]). + reply/1,reply/2]). %% gen callbacks -export( @@ -105,17 +105,21 @@ {'reply', % Reply to a client Client :: client(), Reply :: term()}. -type state_callback_result() :: - {stop, % Stop the server + {'stop', % Stop the server + Reason :: term()} | + {'stop', % Stop the server Reason :: term(), NewStateData :: state_data()} | - {stop, % Stop the server + {'stop', % Stop the server Reason :: term(), Replies :: [reply_operation()] | reply_operation(), NewStateData :: state_data()} | - {next_state, % {next_state,NewState,NewStateData,[]} + {'next_state', % {next_state,State,StateData,StateOps} % Actually useful + StateOps :: [state_op()] | state_op()} | + {'next_state', % {next_state,NewState,NewStateData,[]} NewState :: state(), NewStateData :: state_data()} | - {next_state, % State transition, maybe to the same state + {'next_state', % State transition, maybe to the same state NewState :: state(), NewStateData :: state_data(), StateOps :: [state_op()] | state_op()}. @@ -379,6 +383,10 @@ call(ServerRef, Request, Timeout) -> end. %% Reply from a state machine callback to whom awaits in call/2 +-spec reply(ReplyOperation :: reply_operation()) -> ok. +reply({reply,{_To,_Tag}=Client,Reply}) -> + reply(Client, Reply). +%% -spec reply(Client :: client(), Reply :: term()) -> ok. reply({To,Tag}, Reply) -> Msg = {Tag,Reply}, @@ -742,6 +750,8 @@ loop_events( %% Interpret all callback return value variants loop_event_result(Parent, Debug, S, Events, Event, Result) -> case Result of + {stop,Reason} -> + ?TERMINATE(Reason, Debug, S, [Event|Events]); {stop,Reason,NewStateData} -> ?TERMINATE( Reason, Debug, @@ -764,6 +774,11 @@ loop_event_result(Parent, Debug, S, Events, Event, Result) -> ?TERMINATE( {bad_return_value,{stop,Reason,BadReplies,NewStateData}}, Debug, NewS, Q); + {next_state,StateOps} -> + #{state := State, state_data := StateData} = S, + loop_event_state_ops( + Parent, Debug, S, Events, Event, + State, StateData, StateOps); {next_state,NewState,NewStateData} -> loop_event_state_ops( Parent, Debug, S, Events, Event, @@ -846,7 +861,12 @@ loop_event_state_ops( %% Server helpers collect_init_options(InitOps) -> - collect_init_options(InitOps, state_functions, []). + 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, StateOps) -> {CallbackMode,lists:reverse(StateOps)}; @@ -864,7 +884,12 @@ collect_init_options([InitOp|InitOps] = IOIOs, CallbackMode, StateOps) -> end. collect_state_options(StateOps) -> - collect_state_options(StateOps, false, false, undefined, []). + if + is_list(StateOps) -> + collect_state_options(StateOps, false, false, undefined, []); + true -> + collect_state_options([StateOps], false, false, undefined, []) + end. %% Keep the last of each kind collect_state_options( [], Postpone, Hibernate, Timeout, Operations) -> diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 8b619b6a60..6249ada027 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -1158,11 +1158,10 @@ idle(cast, badreturn, _, _, _Data) -> badreturn; idle({call,_From}, badreturn, _, _, _Data) -> badreturn; -idle({call,From}, {delayed_answer,T}, _, State, Data) -> +idle({call,From}, {delayed_answer,T}, _, _, _) -> receive after T -> - {next_state,State,Data, - [{reply,From,delayed}]} + throw({next_state,{reply,From,delayed}}) end; idle({call,From}, {timeout,Time}, _, State, _Data) -> {next_state,timeout,{From,Time}, @@ -1172,9 +1171,10 @@ idle(Type, Content, PrevState, State, Data) -> undefined -> case Type of {call,From} -> - {next_state,State,Data,[{reply,From,'eh?'}]}; + throw({next_state,[{reply,From,'eh?'}]}); _ -> - {stop,{unexpected,State,PrevState,Type,Content},Data} + throw( + {stop,{unexpected,State,PrevState,Type,Content}}) end; Result -> Result @@ -1208,8 +1208,8 @@ timeout3(_, _, _, State, Data) -> wfor_conf({call,From}, confirm, _, _, Data) -> {next_state,connected,Data, [{reply,From,yes}]}; -wfor_conf(cast, {ping,_,_}, _, State, Data) -> - {next_state,State,Data,[postpone]}; +wfor_conf(cast, {ping,_,_}, _, _, _) -> + {next_state,[postpone]}; wfor_conf(cast, confirm, _, _, Data) -> {next_state,connected,Data}; wfor_conf(Type, Content, PrevState, State, Data) -> @@ -1220,7 +1220,7 @@ wfor_conf(Type, Content, PrevState, State, Data) -> {next_state,idle,Data, [{reply,From,'eh?'}]}; _ -> - {next_state,State,Data} + throw({next_state,[]}) end; Result -> Result @@ -1320,12 +1320,12 @@ handle_common_events(cast, {get,Pid}, _, State, Data) -> {next_state,State,Data}; handle_common_events({call,From}, stop, _, _, Data) -> {stop,normal,[{reply,From,stopped}],Data}; -handle_common_events(cast, stop, _, _, Data) -> - {stop,normal,Data}; +handle_common_events(cast, stop, _, _, _) -> + {stop,normal}; handle_common_events({call,From}, {stop,Reason}, _, _, Data) -> {stop,Reason,{reply,From,stopped},Data}; -handle_common_events(cast, {stop,Reason}, _, _, Data) -> - {stop,Reason,Data}; +handle_common_events(cast, {stop,Reason}, _, _, _) -> + {stop,Reason}; handle_common_events({call,From}, 'alive?', _, State, Data) -> {next_state,State,Data, [{reply,From,yes}]}; -- cgit v1.2.3