diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/stdlib/doc/src/gen_statem.xml | 61 | ||||
-rw-r--r-- | lib/stdlib/src/gen_statem.erl | 46 | ||||
-rw-r--r-- | lib/stdlib/test/gen_statem_SUITE.erl | 44 |
3 files changed, 102 insertions, 49 deletions
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index 9d98763973..f66f399137 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -475,8 +475,9 @@ erlang:'!' -----> Module:StateName/5 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 operations - it is only for <c>{remove_event,<anno>EventPredicate</anno>}</c> - that the order may matter. + it is only for <c>remove_event</c> with + <c><anno>EventPredicate</anno></c> + and for <c>reply_operation()</c> that the order may matter. </p> <taglist> <tag> @@ -583,19 +584,6 @@ erlang:'!' -----> Module:StateName/5 </item> <tag> <c> - {next_state,<anno>StateOps</anno>} - </c> - </tag> - <item>The same as - <c> - {next_state,State,StateData,<anno>StateOps</anno>} - </c>but keeps the old <c>State</c> and <c>StateData</c>. - Believe it or not, but this one has actually - been proven useful to <c>throw/1</c> from deep down - in the state logic. - </item> - <tag> - <c> {next_state,<anno>NewState</anno>,<anno>NewStateData</anno>} </c> </tag> @@ -614,8 +602,47 @@ erlang:'!' -----> Module:StateName/5 </tag> <item>The gen_statem will do a state transition to <c><anno>NewState</anno></c> - (which may be the same as <c>State</c>) - and execute all <c>StateOps</c> + (which may be the same as the current state) + and execute all <c><anno>StateOps</anno></c> + </item> + <tag> + <c> + {keep_state,<anno>NewStateData</anno>}} + </c> + </tag> + <item>The same as + <c> + {keep_state,<anno>NewStateData</anno>,[]} + </c> + </item> + <tag> + <c> + {keep_state,<anno>NewStateData</anno>,<anno>StateOps</anno>} + </c> + </tag> + <item>The gen_statem will keep the current state, or + do a state transition to the current state if you like, + and execute all <c><anno>StateOps</anno></c> + </item> + <tag> + <c> + {keep_state_and_data} + </c> + </tag> + <item>The same as + <c> + {keep_state_and_data,[]} + </c> + </item> + <tag> + <c> + {keep_state_and_data,<anno>StateOps</anno>} + </c> + </tag> + <item>The gen_statem will keep the current state, or + do a state transition to the current state if you like, + also keep the current state data, + and execute all <c><anno>StateOps</anno></c> </item> </taglist> </desc> diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index e03e22b087..3da4443f13 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -114,14 +114,20 @@ Reason :: term(), Replies :: [reply_operation()] | reply_operation(), NewStateData :: state_data()} | - {'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 NewState :: state(), NewStateData :: state_data(), + StateOps :: [state_op()] | state_op()} | + {'keep_state', % {keep_state,NewStateData,[]} + NewStateData :: state_data()} | + {'keep_state', + NewStateData :: state_data(), + StateOps :: [state_op()] | state_op()} | + {'keep_state_and_data'} | % {keep_state_and_data,[]} + {'keep_state_and_data', StateOps :: [state_op()] | state_op()}. %% The state machine init function. It is called only once and @@ -477,7 +483,7 @@ enter(Module, Options, State, StateData, Server, InitOps, Parent) -> S#{callback_mode := CallbackMode}, [], {event,undefined}, % Will be discarded by {postpone,false} - State, StateData, + PrevState, State, StateData, StateOps++[{postpone,false}]); [Reason] -> ?TERMINATE(Reason, Debug, S, []) @@ -749,7 +755,10 @@ loop_events( end. %% Interpret all callback return value variants -loop_event_result(Parent, Debug, S, Events, Event, Result) -> +loop_event_result( + Parent, Debug, + #{state := State, state_data := StateData} = S, + Events, Event, Result) -> case Result of {stop,Reason} -> ?TERMINATE(Reason, Debug, S, [Event|Events]); @@ -775,28 +784,39 @@ 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, - NewState, NewStateData, []); + State, NewState, NewStateData, []); {next_state,NewState,NewStateData,StateOps} when is_list(StateOps) -> loop_event_state_ops( Parent, Debug, S, Events, Event, - NewState, NewStateData, StateOps); + State, NewState, NewStateData, StateOps); + {keep_state,NewStateData} -> + loop_event_state_ops( + Parent, Debug, S, Events, Event, + State, State, NewStateData, []); + {keep_state,NewStateData,StateOps} -> + loop_event_state_ops( + Parent, Debug, S, Events, Event, + State, State, NewStateData, StateOps); + {keep_state_and_data} -> + loop_event_state_ops( + Parent, Debug, S, Events, Event, + State, State, StateData, []); + {keep_state_and_data,StateOps} -> + loop_event_state_ops( + Parent, Debug, S, Events, Event, + State, State, StateData, StateOps); _ -> ?TERMINATE( {bad_return_value,Result}, Debug, S, [Event|Events]) end. loop_event_state_ops( - Parent, Debug0, #{state := State, postponed := P0} = S, Events, Event, - NewState, NewStateData, StateOps) -> + Parent, Debug0, #{postponed := P0} = S, Events, Event, + State, NewState, NewStateData, StateOps) -> case collect_state_options(StateOps) of {Postpone,Hibernate,Timeout,Operations} -> P1 = % Move current event to postponed if Postpone diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 6249ada027..92dc59e843 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -1158,10 +1158,10 @@ idle(cast, badreturn, _, _, _Data) -> badreturn; idle({call,_From}, badreturn, _, _, _Data) -> badreturn; -idle({call,From}, {delayed_answer,T}, _, _, _) -> +idle({call,From}, {delayed_answer,T}, _, _, Data) -> receive after T -> - throw({next_state,{reply,From,delayed}}) + throw({keep_state,Data,{reply,From,delayed}}) end; idle({call,From}, {timeout,Time}, _, State, _Data) -> {next_state,timeout,{From,Time}, @@ -1171,7 +1171,7 @@ idle(Type, Content, PrevState, State, Data) -> undefined -> case Type of {call,From} -> - throw({next_state,[{reply,From,'eh?'}]}); + throw({keep_state,Data,[{reply,From,'eh?'}]}); _ -> throw( {stop,{unexpected,State,PrevState,Type,Content}}) @@ -1209,7 +1209,7 @@ wfor_conf({call,From}, confirm, _, _, Data) -> {next_state,connected,Data, [{reply,From,yes}]}; wfor_conf(cast, {ping,_,_}, _, _, _) -> - {next_state,[postpone]}; + {keep_state_and_data,[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?'}]}; _ -> - throw({next_state,[]}) + throw({keep_state_and_data}) end; Result -> Result @@ -1341,26 +1341,32 @@ handle_common_events(_, _, _, _, _) -> %% states. Note that the state from init/1 is not wrapped %% so both atom and non-atom states are tested. handle_event(Type, Event, PrevState, State, Data) -> - PrevStateName = unwrap(PrevState), - StateName = unwrap(State), - case - ?MODULE:StateName( + PrevStateName = unwrap_state(PrevState), + StateName = unwrap_state(State), + try ?MODULE:StateName( Type, Event, PrevStateName, StateName, Data) of - {next_state,NewState,NewStateData} -> - {next_state,wrap(NewState),NewStateData}; - {next_state,NewState,NewStateData,StateOps} -> - {next_state,wrap(NewState),NewStateData,StateOps}; - Other -> - Other + Result -> + wrap_result(Result) + catch + throw:Result -> + erlang:raise( + throw, wrap_result(Result), erlang:get_stacktrace()) end. -unwrap([State]) -> +unwrap_state([State]) -> State; -unwrap(State) -> +unwrap_state(State) -> State. -wrap(State) -> - [State]. +wrap_result(Result) -> + case Result of + {next_state,NewState,NewStateData} -> + {next_state,[NewState],NewStateData}; + {next_state,NewState,NewStateData,StateOps} -> + {next_state,[NewState],NewStateData,StateOps}; + Other -> + Other + end. |