aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml70
-rw-r--r--lib/stdlib/src/gen_statem.erl23
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl81
3 files changed, 73 insertions, 101 deletions
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</pre>
+- -----> Module:code_change/4</pre>
<p>Events are of different
<seealso marker="#type-event_type">types</seealso>
so the callback functions can know the origin of an event
@@ -91,28 +91,22 @@ erlang:'!' -----> Module:StateName/4
is <c>state_functions</c> the state has to be an atom and
is used as the state function name.
See
- <seealso marker="#Module:StateName/4">
- <c>Module:StateName/4</c>
+ <seealso marker="#Module:StateName/3">
+ <c>Module:StateName/3</c>
</seealso>.
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
- <seealso marker="#Module:code_change/4">
- <c>Module:code_change/4</c>
- </seealso> makes the state name <c>code_change</c>
- 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 <c>code_change</c> as a state name.
+ <seealso marker="#Module:terminate/3">
+ <c>Module:terminate/3</c>
+ </seealso> makes the state name <c>terminate</c> unusable.
</p>
<p>When
<seealso marker="#type-callback_mode">callback_mode</seealso>
is <c>handle_event_function</c> the state can be any term
and the state function name is
- <seealso marker="#Module:handle_event/5">
- <c>Module:handle_event/5</c>
+ <seealso marker="#Module:handle_event/4">
+ <c>Module:handle_event/4</c>
</seealso>.
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.
</p>
- <p>There is a small gotcha in both callback modes regarding
- the state <c>undefined</c> that is used as the previous state
- when the first state function is called. So either do not
- use <c>undefined</c> as a state, or figure out
- the implications yourself.
- </p>
<p>The <c>gen_statem</c> enqueues incoming events in order of arrival
and presents these to the
<seealso marker="#state_function">state function</seealso>
@@ -378,14 +366,14 @@ erlang:'!' -----> Module:StateName/4
<item>The state has to be of type
<seealso marker="#type-state_name"><c>state_name()</c></seealso>
and one callback function per state that is
- <seealso marker="#Module:StateName/4">
- <c>Module:StateName/4</c>
- </seealso> is used. This is the default.
+ <seealso marker="#Module:StateName/3">
+ <c>Module:StateName/3</c>
+ </seealso> is used.
</item>
<tag><c>handle_event_function</c></tag>
<item>The state can be any term and the callback function
- <seealso marker="#Module:handle_event/5">
- <c>Module:handle_event/5</c>
+ <seealso marker="#Module:handle_event/4">
+ <c>Module:handle_event/4</c>
</seealso> is used for all states.
</item>
</taglist>
@@ -1054,9 +1042,10 @@ erlang:'!' -----> Module:StateName/4
<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>
+ <c>State</c> is the initial
+ <seealso marker="#type-state">state()</seealso>
+ and <c>Data</c> the initial server
+ <seealso marker="#type-data">data()</seealso>
</p>
<p>The <seealso marker="#type-action"><c>Actions</c></seealso>
are executed when entering the first
@@ -1076,11 +1065,10 @@ erlang:'!' -----> Module:StateName/4
</func>
<func>
- <name>Module:StateName(EventType, EventContent,
- PrevStateName, Data) -> Result
+ <name>Module:StateName(EventType, EventContent, Data) -> Result
</name>
<name>Module:handle_event(EventType, EventContent,
- PrevState, State, Data) -> Result
+ State, Data) -> Result
</name>
<fsummary>Handle an event</fsummary>
<type>
@@ -1088,10 +1076,7 @@ erlang:'!' -----> Module:StateName/4
<seealso marker="#type-event_type">event_type()</seealso>
</v>
<v>EventContent = term()</v>
- <v>PrevStateName =
- <seealso marker="#type-state_name">state_name()</seealso>
- </v>
- <v>PrevState = State =
+ <v>State = NewState =
<seealso marker="#type-state">state()</seealso>
</v>
<v>Data = NewData =
@@ -1109,9 +1094,9 @@ erlang:'!' -----> Module:StateName/4
<seealso marker="#cast/2">cast/2</seealso> or
as a normal process message one of these functions is called.
If <seealso marker="#type-callback_mode">callback_mode</seealso>
- is <c>state_functions</c> then <c>Module:StateName/4</c> is called,
+ is <c>state_functions</c> then <c>Module:StateName/3</c> is called,
and if it is <c>handle_event_function</c>
- then <c>Module:handle_event/5</c> is called.
+ then <c>Module:handle_event/4</c> is called.
</p>
<p>If <c>EventType</c> is
<seealso marker="#type-event_type"><c>{call,Caller}</c></seealso>
@@ -1126,13 +1111,6 @@ erlang:'!' -----> Module:StateName/4
<c>reply(Caller, Reply)</c>
</seealso>.
</p>
- <p><c>PrevStateName</c> and <c>PrevState</c> 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 <c>gen_statem</c> enters its first state
- this is set to <c>undefined</c>.
- </p>
<p>If this function returns with a new state that
does not match equal (<c>=/=</c>) 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