From 14028aee428c135211e33df77c1bee2fdc128f6e Mon Sep 17 00:00:00 2001 From: Raimo Niskanen Date: Wed, 24 Feb 2016 16:36:24 +0100 Subject: Ditch PrevState StateName/4 -> StateName/3 handle_event/5 -> handle_event/4 --- lib/stdlib/doc/src/gen_statem.xml | 70 +++++++++++-------------------- lib/stdlib/src/gen_statem.erl | 23 ++++------ lib/stdlib/test/gen_statem_SUITE.erl | 81 ++++++++++++++++++------------------ 3 files changed, 73 insertions(+), 101 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index 1e41d616e9..f21610d14c 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -62,12 +62,12 @@ gen_statem:stop -----> Module:terminate/2 gen_statem:call gen_statem:cast erlang:send -erlang:'!' -----> Module:StateName/4 - Module:handle_event/5 +erlang:'!' -----> Module:StateName/3 + Module:handle_event/4 - -----> Module:terminate/3 -- -----> Module:code_change/3 +- -----> Module:code_change/4

Events are of different types so the callback functions can know the origin of an event @@ -91,28 +91,22 @@ erlang:'!' -----> Module:StateName/4 is state_functions the state has to be an atom and is used as the state function name. See - - Module:StateName/4 + + Module:StateName/3 . This gathers all code for a specific state in one function and hence dispatches on state first. Note that in this mode the callback function - - Module:code_change/4 - makes the state name code_change - unusable. Actually you might get away with using it - as a state name but you will have to find a way to separate - the two callback roles e.g by observing that you can force - argument 3 into having different types for the different roles. - This is a slippery slope, though. It is easier - to simply not use code_change as a state name. + + Module:terminate/3 + makes the state name terminate unusable.

When callback_mode is handle_event_function the state can be any term and the state function name is - - Module:handle_event/5 + + Module:handle_event/4 . This makes it easy to dispatch on state or on event as you like but you will have to implement it. @@ -120,12 +114,6 @@ erlang:'!' -----> Module:StateName/4 states so you do not accidentally postpone one event forever creating an infinite busy loop.

-

There is a small gotcha in both callback modes regarding - the state undefined that is used as the previous state - when the first state function is called. So either do not - use undefined as a state, or figure out - the implications yourself. -

The gen_statem enqueues incoming events in order of arrival and presents these to the state function @@ -378,14 +366,14 @@ erlang:'!' -----> Module:StateName/4 The state has to be of type state_name() and one callback function per state that is - - Module:StateName/4 - is used. This is the default. + + Module:StateName/3 + is used. handle_event_function The state can be any term and the callback function - - Module:handle_event/5 + + Module:handle_event/4 is used for all states. @@ -1054,9 +1042,10 @@ erlang:'!' -----> Module:StateName/4 CallbackMode selects the callback_mode(). of the gen_statem. - State is the state() - of the gen_statem and - Data the server data() + State is the initial + state() + and Data the initial server + data()

The Actions are executed when entering the first @@ -1076,11 +1065,10 @@ erlang:'!' -----> Module:StateName/4 - Module:StateName(EventType, EventContent, - PrevStateName, Data) -> Result + Module:StateName(EventType, EventContent, Data) -> Result Module:handle_event(EventType, EventContent, - PrevState, State, Data) -> Result + State, Data) -> Result Handle an event @@ -1088,10 +1076,7 @@ erlang:'!' -----> Module:StateName/4 event_type() EventContent = term() - PrevStateName = - state_name() - - PrevState = State = + State = NewState = state() Data = NewData = @@ -1109,9 +1094,9 @@ erlang:'!' -----> Module:StateName/4 cast/2 or as a normal process message one of these functions is called. If callback_mode - is state_functions then Module:StateName/4 is called, + is state_functions then Module:StateName/3 is called, and if it is handle_event_function - then Module:handle_event/5 is called. + then Module:handle_event/4 is called.

If EventType is {call,Caller} @@ -1126,13 +1111,6 @@ erlang:'!' -----> Module:StateName/4 reply(Caller, Reply) .

-

PrevStateName and PrevState are useful - in some odd cases for example when you want to do something - only at the first event in a state, or when you need to - handle events differently depending on the previous state. - Note that when gen_statem enters its first state - this is set to undefined. -

If this function returns with a new state that does not match equal (=/=) to the current state all postponed events will be retried in the new state. diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index 7fbc1e0f0d..c844b76653 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -184,7 +184,6 @@ -callback state_name( event_type(), EventContent :: term(), - PrevStateName :: state_name(), % Previous state name Data :: data()) -> state_callback_result(). %% @@ -195,7 +194,6 @@ -callback handle_event( event_type(), EventContent :: term(), - PrevState :: state(), % Previous state State :: state(), % Current state Data :: data()) -> state_callback_result(). @@ -234,10 +232,10 @@ [init/1, % One may use enter_loop/5,6,7 instead format_status/2, % Has got a default implementation %% - state_name/4, % Example for callback_mode =:= state_functions: + state_name/3, % Example for callback_mode =:= state_functions: %% there has to be a StateName/5 callback function for every StateName. %% - handle_event/5]). % For callback_mode =:= handle_event_function + handle_event/4]). % For callback_mode =:= handle_event_function %% Type validation functions callback_mode(CallbackMode) -> @@ -518,7 +516,7 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) true -> Name = gen:get_proc_name(Server), Debug = gen:debug_options(Name, Opts), - PrevState = undefined, + OldState = make_ref(), NewActions = if is_list(Actions) -> @@ -530,8 +528,7 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) callback_mode => CallbackMode, module => Module, name => Name, - prev_state => PrevState, - state => PrevState, % Discarded by loop_event_actions + state => OldState, % Discarded by loop_event_actions data => Data, timer => undefined, postponed => [], @@ -539,7 +536,7 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) loop_event_actions( Parent, Debug, S, [], {event,undefined}, % Discarded due to {postpone,false} - PrevState, State, Data, NewActions); + OldState, State, Data, NewActions); false -> erlang:error( badarg, @@ -771,7 +768,6 @@ loop_events( Parent, Debug, #{callback_mode := CallbackMode, module := Module, - prev_state := PrevState, state := State, data := Data} = S, [{Type,Content} = Event|Events] = Q, Timer) -> @@ -780,9 +776,9 @@ loop_events( try case CallbackMode of state_functions -> - Module:State(Type, Content, PrevState, Data); + Module:State(Type, Content, Data); handle_event_function -> - Module:handle_event(Type, Content, PrevState, State, Data) + Module:handle_event(Type, Content, State, Data) end of Result -> loop_event_result( @@ -796,7 +792,7 @@ loop_events( %% of calling a nonexistent state function case erlang:get_stacktrace() of [{Module,State, - [Type,Content,PrevState,Data]=Args, + [Type,Content,Data]=Args, _} |Stacktrace] when CallbackMode =:= state_functions -> @@ -806,7 +802,7 @@ loop_events( Stacktrace, Debug, S, Q); [{Module,handle_event, - [Type,Content,PrevState,State,Data]=Args, + [Type,Content,State,Data]=Args, _} |Stacktrace] when CallbackMode =:= handle_event_function -> @@ -932,7 +928,6 @@ loop_event_actions( loop_events( Parent, NewDebug, S#{ - prev_state := State, state := NewState, data := NewData, timer := Timer, diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 3302171329..27b042c0d8 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -1160,26 +1160,26 @@ terminate(_Reason, _State, _Data) -> %% State functions -idle(cast, {connect,Pid}, _, Data) -> +idle(cast, {connect,Pid}, Data) -> Pid ! accept, {next_state,wfor_conf,Data}; -idle({call,From}, connect, _, Data) -> +idle({call,From}, connect, Data) -> gen_statem:reply(From, accept), {next_state,wfor_conf,Data}; -idle(cast, badreturn, _, _Data) -> +idle(cast, badreturn, _Data) -> badreturn; -idle({call,_From}, badreturn, _, _Data) -> +idle({call,_From}, badreturn, _Data) -> badreturn; -idle({call,From}, {delayed_answer,T}, _, Data) -> +idle({call,From}, {delayed_answer,T}, Data) -> receive after T -> gen_statem:reply({reply,From,delayed}), throw({keep_state,Data}) end; -idle({call,From}, {timeout,Time}, _, _Data) -> +idle({call,From}, {timeout,Time}, _Data) -> {next_state,timeout,{From,Time}, [{timeout,Time,idle}]}; -idle(Type, Content, PrevState, Data) -> +idle(Type, Content, Data) -> case handle_common_events(Type, Content, idle, Data) of undefined -> case Type of @@ -1187,45 +1187,45 @@ idle(Type, Content, PrevState, Data) -> throw({keep_state,Data,[{reply,From,'eh?'}]}); _ -> throw( - {stop,{unexpected,idle,Type,Content,PrevState}}) + {stop,{unexpected,idle,Type,Content}}) end; Result -> Result end. -timeout(timeout, idle, idle, {From,Time}) -> +timeout(timeout, idle, {From,Time}) -> TRef2 = erlang:start_timer(Time, self(), ok), TRefC1 = erlang:start_timer(Time, self(), cancel1), TRefC2 = erlang:start_timer(Time, self(), cancel2), {next_state,timeout2,{From,Time,TRef2}, [{cancel_timer, TRefC1}, {next_event,internal,{cancel_timer,TRefC2}}]}; -timeout(_, _, _, Data) -> +timeout(_, _, Data) -> {keep_state,Data}. timeout2( - internal, {cancel_timer,TRefC2}, timeout, {From,Time,TRef2}) -> + internal, {cancel_timer,TRefC2}, {From,Time,TRef2}) -> Time4 = Time * 4, receive after Time4 -> ok end, {next_state,timeout3,{From,TRef2}, [{cancel_timer,TRefC2}]}; -timeout2(_, _, _, Data) -> +timeout2(_, _, Data) -> {keep_state,Data}. -timeout3(info, {timeout,TRef2,Result}, _, {From,TRef2}) -> +timeout3(info, {timeout,TRef2,Result}, {From,TRef2}) -> gen_statem:reply([{reply,From,Result}]), {next_state,idle,state}; -timeout3(_, _, _, Data) -> +timeout3(_, _, Data) -> {keep_state,Data}. -wfor_conf({call,From}, confirm, _, Data) -> +wfor_conf({call,From}, confirm, Data) -> {next_state,connected,Data, [{reply,From,yes}]}; -wfor_conf(cast, {ping,_,_}, _, _) -> +wfor_conf(cast, {ping,_,_}, _) -> {keep_state_and_data,[postpone]}; -wfor_conf(cast, confirm, _, Data) -> +wfor_conf(cast, confirm, Data) -> {next_state,connected,Data}; -wfor_conf(Type, Content, _, Data) -> +wfor_conf(Type, Content, Data) -> case handle_common_events(Type, Content, wfor_conf, Data) of undefined -> case Type of @@ -1239,21 +1239,21 @@ wfor_conf(Type, Content, _, Data) -> Result end. -connected({call,From}, {msg,Ref}, _, Data) -> +connected({call,From}, {msg,Ref}, Data) -> {keep_state,Data, [{reply,From,{ack,Ref}}]}; -connected(cast, {msg,From,Ref}, _, Data) -> +connected(cast, {msg,From,Ref}, Data) -> From ! {ack,Ref}, {keep_state,Data}; -connected({call,From}, disconnect, _, Data) -> +connected({call,From}, disconnect, Data) -> {next_state,idle,Data, [{reply,From,yes}]}; -connected(cast, disconnect, _, Data) -> +connected(cast, disconnect, Data) -> {next_state,idle,Data}; -connected(cast, {ping,Pid,Tag}, _, Data) -> +connected(cast, {ping,Pid,Tag}, Data) -> Pid ! {pong,Tag}, {keep_state,Data}; -connected(Type, Content, _, Data) -> +connected(Type, Content, Data) -> case handle_common_events(Type, Content, connected, Data) of undefined -> case Type of @@ -1267,9 +1267,9 @@ connected(Type, Content, _, Data) -> Result end. -state0({call,From}, stop, _, Data) -> +state0({call,From}, stop, Data) -> {stop_and_reply,normal,[{reply,From,stopped}],Data}; -state0(Type, Content, _, Data) -> +state0(Type, Content, Data) -> case handle_common_events(Type, Content, state0, Data) of undefined -> {keep_state,Data}; @@ -1277,26 +1277,26 @@ state0(Type, Content, _, Data) -> Result end. -hiber_idle({call,From}, 'alive?', _, Data) -> +hiber_idle({call,From}, 'alive?', Data) -> {keep_state,Data, [{reply,From,'alive!'}]}; -hiber_idle({call,From}, hibernate_sync, _, Data) -> +hiber_idle({call,From}, hibernate_sync, Data) -> {next_state,hiber_wakeup,Data, [{reply,From,hibernating}, hibernate]}; -hiber_idle(info, hibernate_later, _, _) -> +hiber_idle(info, hibernate_later, _) -> Tref = erlang:start_timer(1000, self(), hibernate), {keep_state,Tref}; -hiber_idle(info, hibernate_now, _, Data) -> +hiber_idle(info, hibernate_now, Data) -> {keep_state,Data, [hibernate]}; -hiber_idle(info, {timeout,Tref,hibernate}, _, Tref) -> +hiber_idle(info, {timeout,Tref,hibernate}, Tref) -> {keep_state,[], [hibernate]}; -hiber_idle(cast, hibernate_async, _, Data) -> +hiber_idle(cast, hibernate_async, Data) -> {next_state,hiber_wakeup,Data, [hibernate]}; -hiber_idle(Type, Content, _, Data) -> +hiber_idle(Type, Content, Data) -> case handle_common_events(Type, Content, hiber_idle, Data) of undefined -> {keep_state,Data}; @@ -1304,19 +1304,19 @@ hiber_idle(Type, Content, _, Data) -> Result end. -hiber_wakeup({call,From}, wakeup_sync, _, Data) -> +hiber_wakeup({call,From}, wakeup_sync, Data) -> {next_state,hiber_idle,Data, [{reply,From,good_morning}]}; -hiber_wakeup({call,From}, snooze_sync, _, Data) -> +hiber_wakeup({call,From}, snooze_sync, Data) -> {keep_state,Data, [{reply,From,please_just_five_more}, hibernate]}; -hiber_wakeup(cast, wakeup_async, _, Data) -> +hiber_wakeup(cast, wakeup_async, Data) -> {next_state,hiber_idle,Data}; -hiber_wakeup(cast, snooze_async, _, Data) -> +hiber_wakeup(cast, snooze_async, Data) -> {keep_state,Data, [hibernate]}; -hiber_wakeup(Type, Content, _, Data) -> +hiber_wakeup(Type, Content, Data) -> case handle_common_events(Type, Content, hiber_wakeup, Data) of undefined -> {keep_state,Data}; @@ -1353,10 +1353,9 @@ handle_common_events(_, _, _, _) -> %% Wrap the state in a 1 element list just to test non-atom %% 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_state(PrevState), +handle_event(Type, Event, State, Data) -> StateName = unwrap_state(State), - try ?MODULE:StateName(Type, Event, PrevStateName, Data) of + try ?MODULE:StateName(Type, Event, Data) of Result -> wrap_result(Result) catch -- cgit v1.2.3