Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: erlang/otp
base: maint
...
head fork: fishcakez/otp
Checking mergeability… Don’t worry, you can still create the pull request.
  • 1 commit
  • 9 files changed
  • 0 commit comments
  • 1 contributor
Commits on Jul 31, 2013
@fishcakez fishcakez Fix sys:get_state/2 and sys:replace_state/3 when sys suspended
Adds two new system callbacks Module:system_get_state/1 and
Module:system_replace_state/2 to allow sys:get_state/2 and
sys:replace_state/3 to used be when a process is suspended by sys.

The previous behaviour of intercepting the system message and passing
a tuple of size 2 as the last argument to sys:handle_system_msg/6 will
continue to work when a process is not suspend by sys. If the process is
suspended by sys and the new callbacks are not exported the process will
not crash but an undef error will be returned to the caller.
33cadc8
View
38 lib/stdlib/doc/src/sys.xml
@@ -246,6 +246,7 @@
<c>{Module, Id, HandlerState}</c>, where <c>Module</c> is the event handler's module name,
<c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without
an ID), and <c>HandlerState</c> is the handler's state.</p>
+ <p><c>Mod:system_get_state/1</c> will be called in the target process to get the <c>State</c>.</p>
<p>To obtain more information about a process, including its state, see
<seealso marker="#get_status-1">get_status/1</seealso> and
<seealso marker="#get_status-2">get_status/2</seealso>.</p>
@@ -289,6 +290,8 @@
function means that only the state of the particular event handler it was working on when it
failed or crashed is unchanged; it can still succeed in changing the states of other event
handlers registered in the same <c>gen_event</c> process.</p>
+ <p><c>Mod:system_replace_state/2</c> will be called in the target process to replace the
+ <c>State</c> using <c>StateFun</c>.</p>
</desc>
</func>
<func>
@@ -375,8 +378,9 @@
process continues the execution, or
<c><anno>Module</anno>:system_terminate(Reason, <anno>Parent</anno>, <anno>Debug</anno>, <anno>Misc</anno>)</c> if
the process should terminate. The <c><anno>Module</anno></c> must export
- <c>system_continue/3</c>, <c>system_terminate/4</c>, and
- <c>system_code_change/4</c> (see below).
+ <c>system_continue/3</c>, <c>system_terminate/4</c>,
+ <c>system_code_change/4</c>, <c>system_get_state/1</c> and
+ <c>system_replace_state/2</c> (see below).
</p>
<p>The <c><anno>Misc</anno></c> argument can be used to save internal data
in a process, for example its state. It is sent to
@@ -444,6 +448,36 @@
defined, the atom <c>undefined</c> is sent.</p>
</desc>
</func>
+ <func>
+ <name>Mod:system_get_state(Misc) -> {ok, State, NMisc}</name>
+ <fsummary>Called when the process should return its current state</fsummary>
+ <type>
+ <v>Misc = term()</v>
+ <v>State = term()</v>
+ <v>NMisc = term()</v>
+ </type>
+ <desc>
+ <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
+ should return a term that reflects its current state. <c>State</c> is the
+ value returned by <c>sys:get_state/2</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>Mod:system_replace_state(StateFun, Misc) -> {ok, NState, NMisc}</name>
+ <fsummary>Called when the process should replace is state</fsummary>
+ <type>
+ <v>StateFun = fun((State :: term()) -> NState)</v>
+ <v>Misc = term()</v>
+ <v>NState = term()</v>
+ <v>NMisc = term()</v>
+ </type>
+ <desc>
+ <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
+ should replace its current state. If the <c>StateFun</c> throws an exception
+ it should be caught and the current state returned. <c>NState</c> is the value
+ returned by <c>sys:replace_state/3</c>.</p>
+ </desc>
+ </func>
</funcs>
</erlref>
View
38 lib/stdlib/src/gen_event.erl
@@ -40,6 +40,8 @@
system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
-export_type([handler/0, handler_args/0, add_handler_ret/0,
@@ -229,24 +231,6 @@ wake_hib(Parent, ServerName, MSL, Debug) ->
fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
receive
- {system, From, get_state} ->
- States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL],
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {States, [ServerName, MSL, Hib]}, Hib);
- {system, From, {replace_state, StateFun}} ->
- {NMSL, NStates} =
- lists:unzip([begin
- Cur = {Mod,Id,State},
- try
- NState = {Mod,Id,NS} = StateFun(Cur),
- {HS#handler{state=NS}, NState}
- catch
- _:_ ->
- {HS, Cur}
- end
- end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NStates, [ServerName, NMSL, Hib]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[ServerName, MSL, Hib],Hib);
@@ -383,6 +367,24 @@ system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) ->
MSL),
{ok, [ServerName, MSL1, Hib]}.
+system_get_state([_ServerName, MSL, _Hib] = GenState) ->
+ States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL],
+ {ok, States, GenState}.
+
+system_replace_state(StateFun, [ServerName, MSL, Hib]) ->
+ {NMSL, NStates} =
+ lists:unzip([begin
+ Cur = {Mod,Id,State},
+ try
+ NState = {Mod,Id,NS} = StateFun(Cur),
+ {HS#handler{state=NS}, NState}
+ catch
+ _:_ ->
+ {HS, Cur}
+ end
+ end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
+ {ok, NStates, [ServerName, NMSL, Hib]}.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
View
26 lib/stdlib/src/gen_fsm.erl
@@ -118,6 +118,8 @@
system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
-import(error_logger, [format/2]).
@@ -422,17 +424,6 @@ wake_hib(Parent, Name, StateName, StateData, Mod, Debug) ->
decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) ->
case Msg of
- {system, From, get_state} ->
- Misc = [Name, StateName, StateData, Mod, Time],
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {{StateName, StateData}, Misc}, Hib);
- {system, From, {replace_state, StateFun}} ->
- State = {StateName, StateData},
- NState = {NStateName, NStateData} = try StateFun(State)
- catch _:_ -> State end,
- NMisc = [Name, NStateName, NStateData, Mod, Time],
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NState, NMisc}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, StateName, StateData, Mod, Time], Hib);
@@ -467,6 +458,19 @@ system_code_change([Name, StateName, StateData, Mod, Time],
Else -> Else
end.
+system_get_state([_Name, StateName, StateData, _Mod, _Time] = GenState) ->
+ {ok, {StateName, StateData}, GenState}.
+
+system_replace_state(StateFun,
+ [Name, StateName, StateData, Mod, Time] = GenState) ->
+ try StateFun({StateName, StateData}) of
+ {NStateName, NStateData} = Result ->
+ {ok, Result, [Name, NStateName, NStateData, Mod, Time]}
+ catch
+ _:_ ->
+ {ok, {StateName, StateData}, GenState}
+ end.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
View
21 lib/stdlib/src/gen_server.erl
@@ -98,6 +98,8 @@
-export([system_continue/3,
system_terminate/4,
system_code_change/4,
+ system_get_state/1,
+ system_replace_state/2,
format_status/2]).
%% Internal exports
@@ -372,13 +374,6 @@ wake_hib(Parent, Name, State, Mod, Debug) ->
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
- {system, From, get_state} ->
- sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
- {State, [Name, State, Mod, Time]}, Hib);
- {system, From, {replace_state, StateFun}} ->
- NState = try StateFun(State) catch _:_ -> State end,
- sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
- {NState, [Name, NState, Mod, Time]}, Hib);
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
@@ -687,6 +682,18 @@ system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
Else -> Else
end.
+system_get_state([_Name, State, _Mod, _Time] = GenState) ->
+ {ok, State, GenState}.
+
+system_replace_state(StateFun, [Name, State, Mod, Time] = GenState) ->
+ try StateFun(State) of
+ NState ->
+ {ok, NState, [Name, NState, Mod, Time]}
+ catch
+ _:_ ->
+ {ok, State, GenState}
+ end.
+
%%-----------------------------------------------------------------
%% Format debug messages. Print them as the call-back module sees
%% them, not as the real erlang messages. Use trace for that.
View
26 lib/stdlib/src/sys.erl
@@ -390,10 +390,20 @@ do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) ->
{suspended, ok, Debug, Misc};
do_cmd(_, resume, _Parent, _Mod, Debug, Misc) ->
{running, ok, Debug, Misc};
+%% For backward compatibility the State could be included in a tuple with Misc.
do_cmd(SysState, get_state, _Parent, _Mod, Debug, {State, Misc}) ->
{SysState, State, Debug, Misc};
+do_cmd(SysState, get_state, _Parent, Mod, Debug, Misc) ->
+ {Res, NMisc} = do_get_state(Mod, Misc),
+ {SysState, Res, Debug, NMisc};
+%% For backward compatibility the State could have been replaced by Mod and
+%% included in a tuple with Misc. In this case the system message was
+%% {replace_state, StateFun} but replace_state is passed by Mod.
do_cmd(SysState, replace_state, _Parent, _Mod, Debug, {State, Misc}) ->
{SysState, State, Debug, Misc};
+do_cmd(SysState, {replace_state, StateFun}, _Parent, Mod, Debug, Misc) ->
+ {Res, NMisc} = do_replace_state(StateFun, Mod, Misc),
+ {SysState, Res, Debug, NMisc};
do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
Res = get_status(SysState, Parent, Mod, Debug, Misc),
{SysState, Res, Debug, Misc};
@@ -407,6 +417,22 @@ do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
do_cmd(SysState, Other, _Parent, _Mod, Debug, Misc) ->
{SysState, {error, {unknown_system_msg, Other}}, Debug, Misc}.
+do_get_state(Mod, Misc) ->
+ case catch Mod:system_get_state(Misc) of
+ {ok, State, NMisc} ->
+ {State, NMisc};
+ {'EXIT', Reason} ->
+ {{error, Reason}, Misc}
+ end.
+
+do_replace_state(StateFun, Mod, Misc) ->
+ case catch Mod:system_replace_state(StateFun, Misc) of
+ {ok, State, NMisc} ->
+ {State, NMisc};
+ {'EXIT', Reason} ->
+ {{error, Reason}, Misc}
+ end.
+
get_status(SysState, Parent, Mod, Debug, Misc) ->
PDict = get(),
FmtMisc =
View
11 lib/stdlib/test/gen_event_SUITE.erl
@@ -974,6 +974,10 @@ get_state(Config) when is_list(Config) ->
[{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result1),
Result2 = sys:get_state(Pid, 5000),
[{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result2),
+ ok = sys:suspend(Pid),
+ Result3 = sys:get_state(Pid),
+ [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result3),
+ ok = sys:resume(Pid),
ok = gen_event:stop(Pid),
ok.
@@ -998,4 +1002,11 @@ replace_state(Config) when is_list(Config) ->
Replace3 = fun(_) -> exit(fail) end,
[{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace3),
[{dummy1_h,false,NState2}] = sys:get_state(Pid),
+ %% verify state replaced if process sys suspended
+ NState3 = "replaced again and again",
+ Replace4 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState3) end,
+ ok = sys:suspend(Pid),
+ [{dummy1_h,false,NState3}] = sys:replace_state(Pid, Replace4),
+ ok = sys:resume(Pid),
+ [{dummy1_h,false,NState3}] = sys:get_state(Pid),
ok.
View
16 lib/stdlib/test/gen_fsm_SUITE.erl
@@ -426,6 +426,14 @@ get_state(Config) when is_list(Config) ->
{idle, State} = sys:get_state(gfsm),
{idle, State} = sys:get_state(gfsm, 5000),
stop_it(Pid2),
+
+ %% check that get_state works when pid is sys suspended
+ {ok, Pid3} = gen_fsm:start(gen_fsm_SUITE, {state_data, State}, []),
+ {idle, State} = sys:get_state(Pid3),
+ ok = sys:suspend(Pid3),
+ {idle, State} = sys:get_state(Pid3, 5000),
+ ok = sys:resume(Pid3),
+ stop_it(Pid3),
ok.
replace_state(Config) when is_list(Config) ->
@@ -444,6 +452,14 @@ replace_state(Config) when is_list(Config) ->
Replace3 = fun(_) -> error(fail) end,
{state0, NState2} = sys:replace_state(Pid, Replace3),
{state0, NState2} = sys:get_state(Pid),
+ %% verify state replaced if process sys suspended
+ ok = sys:suspend(Pid),
+ Suffix2 = " and again",
+ NState3 = NState2 ++ Suffix2,
+ Replace3 = fun({StateName, _}) -> {StateName, NState3} end,
+ {state0, NState3} = sys:replace_state(Pid, Replace3),
+ ok = sys:resume(Pid),
+ NState3 = sys:get_state(Pid, 5000),
stop_it(Pid),
ok.
View
11 lib/stdlib/test/gen_server_SUITE.erl
@@ -1049,6 +1049,9 @@ get_state(Config) when is_list(Config) ->
{ok, Pid} = gen_server:start_link(?MODULE, {state,State}, []),
State = sys:get_state(Pid),
State = sys:get_state(Pid, 5000),
+ ok = sys:suspend(Pid),
+ State = sys:get_state(Pid),
+ ok = sys:resume(Pid),
ok.
%% Verify that sys:replace_state correctly replaces gen_server state
@@ -1077,6 +1080,14 @@ replace_state(Config) when is_list(Config) ->
Replace3 = fun(_) -> throw(fail) end,
NState2 = sys:replace_state(Pid, Replace3),
NState2 = sys:get_state(Pid, 5000),
+ %% verify state replaced if process sys suspended
+ ok = sys:suspend(Pid),
+ Suffix2 = " and again",
+ NState3 = NState2 ++ Suffix2,
+ Replace3 = fun(S) -> S ++ Suffix2 end,
+ NState3 = sys:replace_state(Pid, Replace3),
+ ok = sys:resume(Pid),
+ NState3 = sys:get_state(Pid, 5000),
ok.
%% Test that the time for a huge message queue is not
View
38 system/doc/design_principles/spec_proc.xml
@@ -180,6 +180,18 @@ system_continue(Parent, Deb, Chs) ->
system_terminate(Reason, Parent, Deb, Chs) ->
exit(Reason).
+system_get_state(Chs) ->
+ {ok, Chs, Chs}.
+
+system_replace_state(StateFun, Chs) ->
+ try StateFun(Chs) of
+ Chs2 ->
+ {ok, Chs2, Chs2}
+ catch
+ _:_ ->
+ {ok, Chs, Chs}
+ end.
+
write_debug(Dev, Event, Name) ->
io:format(Dev, "~p event = ~p~n", [Name, Event]).</pre>
<p>Example on how the simple debugging functions in <c>sys</c> can
@@ -353,12 +365,19 @@ sys:handle_system_msg(Request, From, Parent, Module, Deb, State)</code>
message and then call:</p>
<code type="none">
Module:system_continue(Parent, Deb, State)</code>
- <p>if process execution should continue, or:</p>
+ <p>if process execution should continue, or if the process should
+ terminate:</p>
<code type="none">
Module:system_terminate(Reason, Parent, Deb, State)</code>
<p>if the process should terminate. Note that a process in a
supervision tree is expected to terminate with the same reason as
its parent.</p>
+ <p>If the process should return its state sysHandle_system_msg will call:</p>
+ <code type="none">
+Module:system_get_state(State)</code>
+ <p>Or if the process should replace its state using the fun <c>StateFun</c>:</p>
+ <code type="none">
+Module:system_replace_state(StateFun, State)</code>
<list type="bulleted">
<item><c>Request</c> and <c>From</c> should be passed as-is from
the system message to the call to <c>handle_system_msg</c>.</item>
@@ -366,7 +385,8 @@ Module:system_terminate(Reason, Parent, Deb, State)</code>
<item><c>Module</c> is the name of the module.</item>
<item><c>Deb</c> is the debug structure.</item>
<item><c>State</c> is a term describing the internal state and
- is passed to <c>system_continue</c>/<c>system_terminate</c>.</item>
+ is passed to <c>system_continue</c>/<c>system_terminate</c>/
+ <c>system_get_state</c>/<c>system_replace_state</c>.</item>
</list>
<p>In the example:</p>
<code type="none">
@@ -383,7 +403,19 @@ system_continue(Parent, Deb, Chs) ->
loop(Chs, Parent, Deb).
system_terminate(Reason, Parent, Deb, Chs) ->
- exit(Reason).</code>
+ exit(Reason).
+
+system_get_state(Chs) ->
+ {ok, Chs, Chs}.
+
+system_replace_state(StateFun, Chs) ->
+ try StateFun(Chs) of
+ Chs2 ->
+ {ok, Chs2, Chs2}
+ catch
+ _:_ ->
+ {ok, Chs, Chs}
+ end.</code>
<p>If the special process is set to trap exits, note that if
the parent process terminates, the expected behavior is to
terminate with the same reason:</p>

No commit comments for this range

Something went wrong with that request. Please try again.