aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaimo Niskanen <[email protected]>2016-02-18 16:06:57 +0100
committerRaimo Niskanen <[email protected]>2016-02-18 16:06:57 +0100
commit82f34a7a9de85b4afc0dac4c9c426939264c5039 (patch)
tree84ddd5f36e592d3bd5fba9349e2666cbde1cb2be
parentfdd72bc94149c7e279b5c4fd5a040af1886a4c72 (diff)
downloadotp-82f34a7a9de85b4afc0dac4c9c426939264c5039.tar.gz
otp-82f34a7a9de85b4afc0dac4c9c426939264c5039.tar.bz2
otp-82f34a7a9de85b4afc0dac4c9c426939264c5039.zip
Write some convenience helpers
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml23
-rw-r--r--lib/stdlib/src/gen_statem.erl39
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl24
3 files changed, 67 insertions, 19 deletions
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
@@ -535,6 +535,13 @@ erlang:'!' -----> Module:StateName/5
<desc>
<taglist>
<tag>
+ <c>{stop,<anno>Reason</anno>}</c>
+ </tag>
+ <item>The same as
+ <c>{stop,<anno>Reason</anno>,[],StateData}</c>
+ but keeps the old <c>StateData</c>.
+ </item>
+ <tag>
<c>{stop,<anno>Reason</anno>,<anno>NewStateData</anno>}</c>
</tag>
<item>The same as
@@ -553,6 +560,19 @@ 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>
@@ -805,6 +825,7 @@ erlang:'!' -----> Module:StateName/5
</func>
<func>
+ <name name="reply" arity="1" />
<name name="reply" arity="2" />
<fsummary>Send a reply to a client</fsummary>
<desc>
@@ -820,6 +841,8 @@ erlang:'!' -----> Module:StateName/5
<c>{call,<anno>Client</anno>}</c>
</seealso> argument to the
<seealso marker="#state_function">state function</seealso>.
+ <c><anno>Client</anno></c> and <c><anno>Reply</anno></c>
+ an also be specified using <c><anno>ReplyOperation</anno></c>.
</p>
<note>
<p>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}]};