Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 1 commit
  • 9 files changed
  • 0 comments
  • 1 contributor
Jul 31, 2013
James Fish 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
38  lib/stdlib/doc/src/sys.xml
@@ -246,6 +246,7 @@
246 246
           <c>{Module, Id, HandlerState}</c>, where <c>Module</c> is the event handler's module name,
247 247
           <c>Id</c> is the handler's ID (which is the value <c>false</c> if it was registered without
248 248
           an ID), and <c>HandlerState</c> is the handler's state.</p>
  249
+        <p><c>Mod:system_get_state/1</c> will be called in the target process to get the <c>State</c>.</p>
249 250
         <p>To obtain more information about a process, including its state, see
250 251
           <seealso marker="#get_status-1">get_status/1</seealso> and
251 252
           <seealso marker="#get_status-2">get_status/2</seealso>.</p>
@@ -289,6 +290,8 @@
289 290
           function means that only the state of the particular event handler it was working on when it
290 291
           failed or crashed is unchanged; it can still succeed in changing the states of other event
291 292
           handlers registered in the same <c>gen_event</c> process.</p>
  293
+      <p><c>Mod:system_replace_state/2</c> will be called in the target process to replace the
  294
+        <c>State</c> using <c>StateFun</c>.</p>
292 295
       </desc>
293 296
     </func>
294 297
     <func>
@@ -375,8 +378,9 @@
375 378
           process continues the execution, or
376 379
           <c><anno>Module</anno>:system_terminate(Reason, <anno>Parent</anno>, <anno>Debug</anno>, <anno>Misc</anno>)</c> if
377 380
           the process should terminate. The <c><anno>Module</anno></c> must export
378  
-          <c>system_continue/3</c>, <c>system_terminate/4</c>, and
379  
-          <c>system_code_change/4</c> (see below).
  381
+          <c>system_continue/3</c>, <c>system_terminate/4</c>,
  382
+          <c>system_code_change/4</c>, <c>system_get_state/1</c> and
  383
+          <c>system_replace_state/2</c> (see below).
380 384
           </p>
381 385
         <p>The <c><anno>Misc</anno></c> argument can be used to save internal data
382 386
           in a process, for example its state. It is sent to
@@ -444,6 +448,36 @@
444 448
           defined, the atom <c>undefined</c> is sent.</p>
445 449
       </desc>
446 450
     </func>
  451
+    <func>
  452
+      <name>Mod:system_get_state(Misc) -> {ok, State, NMisc}</name>
  453
+      <fsummary>Called when the process should return its current state</fsummary>
  454
+      <type>
  455
+        <v>Misc = term()</v>
  456
+        <v>State = term()</v>
  457
+        <v>NMisc = term()</v>
  458
+    </type>
  459
+      <desc>
  460
+        <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
  461
+          should return a term that reflects its current state. <c>State</c> is the
  462
+          value returned by <c>sys:get_state/2</c>.</p>
  463
+      </desc>
  464
+    </func>
  465
+    <func>
  466
+      <name>Mod:system_replace_state(StateFun, Misc) -> {ok, NState, NMisc}</name>
  467
+      <fsummary>Called when the process should replace is state</fsummary>
  468
+      <type>
  469
+        <v>StateFun = fun((State :: term()) -> NState)</v>
  470
+        <v>Misc = term()</v>
  471
+        <v>NState = term()</v>
  472
+        <v>NMisc = term()</v>
  473
+    </type>
  474
+      <desc>
  475
+        <p>This function is called from <c>sys:handle_system_msg/6</c> when the process
  476
+         should replace its current state. If the <c>StateFun</c> throws an exception
  477
+         it should be caught and the current state returned. <c>NState</c> is the value
  478
+         returned by <c>sys:replace_state/3</c>.</p>
  479
+      </desc>
  480
+    </func>
447 481
   </funcs>
448 482
 </erlref>
449 483
 
38  lib/stdlib/src/gen_event.erl
@@ -40,6 +40,8 @@
40 40
 	 system_continue/3,
41 41
 	 system_terminate/4,
42 42
 	 system_code_change/4,
  43
+	 system_get_state/1,
  44
+	 system_replace_state/2,
43 45
 	 format_status/2]).
44 46
 
45 47
 -export_type([handler/0, handler_args/0, add_handler_ret/0,
@@ -229,24 +231,6 @@ wake_hib(Parent, ServerName, MSL, Debug) ->
229 231
 
230 232
 fetch_msg(Parent, ServerName, MSL, Debug, Hib) ->
231 233
     receive
232  
-	{system, From, get_state} ->
233  
-	    States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL],
234  
-	    sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
235  
-				  {States, [ServerName, MSL, Hib]}, Hib);
236  
-	{system, From, {replace_state, StateFun}} ->
237  
-	    {NMSL, NStates} =
238  
-		lists:unzip([begin
239  
-				 Cur = {Mod,Id,State},
240  
-				 try
241  
-				     NState = {Mod,Id,NS} = StateFun(Cur),
242  
-				     {HS#handler{state=NS}, NState}
243  
-				 catch
244  
-				     _:_ ->
245  
-					 {HS, Cur}
246  
-				 end
247  
-			     end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
248  
-	    sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
249  
-				  {NStates, [ServerName, NMSL, Hib]}, Hib);
250 234
 	{system, From, Req} ->
251 235
 	    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
252 236
 				  [ServerName, MSL, Hib],Hib);
@@ -383,6 +367,24 @@ system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) ->
383 367
 		    MSL),
384 368
     {ok, [ServerName, MSL1, Hib]}.
385 369
 
  370
+system_get_state([_ServerName, MSL, _Hib] = GenState) ->
  371
+    States = [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL],
  372
+    {ok, States, GenState}.
  373
+
  374
+system_replace_state(StateFun, [ServerName, MSL, Hib]) ->
  375
+    {NMSL, NStates} =
  376
+		lists:unzip([begin
  377
+				 Cur = {Mod,Id,State},
  378
+				 try
  379
+				     NState = {Mod,Id,NS} = StateFun(Cur),
  380
+				     {HS#handler{state=NS}, NState}
  381
+				 catch
  382
+				     _:_ ->
  383
+					 {HS, Cur}
  384
+				 end
  385
+			     end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]),
  386
+    {ok, NStates, [ServerName, NMSL, Hib]}.
  387
+
386 388
 %%-----------------------------------------------------------------
387 389
 %% Format debug messages.  Print them as the call-back module sees
388 390
 %% them, not as the real erlang messages.  Use trace for that.
26  lib/stdlib/src/gen_fsm.erl
@@ -118,6 +118,8 @@
118 118
 	 system_continue/3,
119 119
 	 system_terminate/4,
120 120
 	 system_code_change/4,
  121
+	 system_get_state/1,
  122
+	 system_replace_state/2,
121 123
 	 format_status/2]).
122 124
 
123 125
 -import(error_logger, [format/2]).
@@ -422,17 +424,6 @@ wake_hib(Parent, Name, StateName, StateData, Mod, Debug) ->
422 424
 
423 425
 decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) ->
424 426
     case Msg of
425  
-	{system, From, get_state} ->
426  
-	    Misc = [Name, StateName, StateData, Mod, Time],
427  
-	    sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
428  
-				  {{StateName, StateData}, Misc}, Hib);
429  
-	{system, From, {replace_state, StateFun}} ->
430  
-	    State = {StateName, StateData},
431  
-	    NState = {NStateName, NStateData} = try StateFun(State)
432  
-						catch _:_ -> State end,
433  
-	    NMisc = [Name, NStateName, NStateData, Mod, Time],
434  
-	    sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
435  
-				  {NState, NMisc}, Hib);
436 427
         {system, From, Req} ->
437 428
 	    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
438 429
 				  [Name, StateName, StateData, Mod, Time], Hib);
@@ -467,6 +458,19 @@ system_code_change([Name, StateName, StateData, Mod, Time],
467 458
 	Else -> Else
468 459
     end.
469 460
 
  461
+system_get_state([_Name, StateName, StateData, _Mod, _Time] = GenState) ->
  462
+    {ok, {StateName, StateData}, GenState}.
  463
+
  464
+system_replace_state(StateFun,
  465
+		     [Name, StateName, StateData, Mod, Time] = GenState) ->
  466
+    try StateFun({StateName, StateData}) of
  467
+	{NStateName, NStateData} = Result ->
  468
+	    {ok, Result, [Name, NStateName, NStateData, Mod, Time]}
  469
+    catch
  470
+	_:_ ->
  471
+	    {ok, {StateName, StateData}, GenState}
  472
+    end.
  473
+
470 474
 %%-----------------------------------------------------------------
471 475
 %% Format debug messages.  Print them as the call-back module sees
472 476
 %% them, not as the real erlang messages.  Use trace for that.
21  lib/stdlib/src/gen_server.erl
@@ -98,6 +98,8 @@
98 98
 -export([system_continue/3,
99 99
 	 system_terminate/4,
100 100
 	 system_code_change/4,
  101
+	 system_get_state/1,
  102
+	 system_replace_state/2,
101 103
 	 format_status/2]).
102 104
 
103 105
 %% Internal exports
@@ -372,13 +374,6 @@ wake_hib(Parent, Name, State, Mod, Debug) ->
372 374
 
373 375
 decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
374 376
     case Msg of
375  
-	{system, From, get_state} ->
376  
-	    sys:handle_system_msg(get_state, From, Parent, ?MODULE, Debug,
377  
-				  {State, [Name, State, Mod, Time]}, Hib);
378  
-	{system, From, {replace_state, StateFun}} ->
379  
-	    NState = try StateFun(State) catch _:_ -> State end,
380  
-	    sys:handle_system_msg(replace_state, From, Parent, ?MODULE, Debug,
381  
-				  {NState, [Name, NState, Mod, Time]}, Hib);
382 377
 	{system, From, Req} ->
383 378
 	    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
384 379
 				  [Name, State, Mod, Time], Hib);
@@ -687,6 +682,18 @@ system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
687 682
 	Else -> Else
688 683
     end.
689 684
 
  685
+system_get_state([_Name, State, _Mod, _Time] = GenState) ->
  686
+    {ok, State, GenState}.
  687
+
  688
+system_replace_state(StateFun, [Name, State, Mod, Time] = GenState) ->
  689
+    try StateFun(State) of
  690
+	NState ->
  691
+	    {ok, NState, [Name, NState, Mod, Time]}
  692
+    catch
  693
+	_:_ ->
  694
+	    {ok, State, GenState}
  695
+    end.
  696
+
690 697
 %%-----------------------------------------------------------------
691 698
 %% Format debug messages.  Print them as the call-back module sees
692 699
 %% them, not as the real erlang messages.  Use trace for that.
26  lib/stdlib/src/sys.erl
@@ -390,10 +390,20 @@ do_cmd(_, suspend, _Parent, _Mod, Debug, Misc) ->
390 390
     {suspended, ok, Debug, Misc};
391 391
 do_cmd(_, resume, _Parent, _Mod, Debug, Misc) ->
392 392
     {running, ok, Debug, Misc};
  393
+%% For backward compatibility the State could be included in a tuple with Misc.
393 394
 do_cmd(SysState, get_state, _Parent, _Mod, Debug, {State, Misc}) ->
394 395
     {SysState, State, Debug, Misc};
  396
+do_cmd(SysState, get_state, _Parent, Mod, Debug, Misc) ->
  397
+    {Res, NMisc} = do_get_state(Mod, Misc),
  398
+    {SysState, Res, Debug, NMisc};
  399
+%% For backward compatibility the State could have been replaced by Mod and
  400
+%% included in a tuple with Misc. In this case the system message was
  401
+%% {replace_state, StateFun} but replace_state is passed by Mod.
395 402
 do_cmd(SysState, replace_state, _Parent, _Mod, Debug, {State, Misc}) ->
396 403
     {SysState, State, Debug, Misc};
  404
+do_cmd(SysState, {replace_state, StateFun},  _Parent, Mod, Debug, Misc) ->
  405
+    {Res, NMisc} = do_replace_state(StateFun, Mod, Misc),
  406
+    {SysState, Res, Debug, NMisc};
397 407
 do_cmd(SysState, get_status, Parent, Mod, Debug, Misc) ->
398 408
     Res = get_status(SysState, Parent, Mod, Debug, Misc),
399 409
     {SysState, Res, Debug, Misc};
@@ -407,6 +417,22 @@ do_cmd(suspended, {change_code, Module, Vsn, Extra}, _Parent,
407 417
 do_cmd(SysState, Other, _Parent, _Mod, Debug, Misc) ->
408 418
     {SysState, {error, {unknown_system_msg, Other}}, Debug, Misc}.
409 419
 
  420
+do_get_state(Mod, Misc) ->
  421
+    case catch Mod:system_get_state(Misc) of
  422
+	{ok, State, NMisc} ->
  423
+	    {State, NMisc};
  424
+	{'EXIT', Reason} ->
  425
+	    {{error, Reason}, Misc}
  426
+    end.
  427
+
  428
+do_replace_state(StateFun, Mod, Misc) ->
  429
+    case catch Mod:system_replace_state(StateFun, Misc) of
  430
+	{ok, State, NMisc} ->
  431
+	    {State, NMisc};
  432
+	{'EXIT', Reason} ->
  433
+	    {{error, Reason}, Misc}
  434
+    end.
  435
+
410 436
 get_status(SysState, Parent, Mod, Debug, Misc) ->
411 437
     PDict = get(),
412 438
     FmtMisc =
11  lib/stdlib/test/gen_event_SUITE.erl
@@ -974,6 +974,10 @@ get_state(Config) when is_list(Config) ->
974 974
     [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result1),
975 975
     Result2 = sys:get_state(Pid, 5000),
976 976
     [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result2),
  977
+    ok = sys:suspend(Pid),
  978
+    Result3 = sys:get_state(Pid),
  979
+    [{dummy1_h,false,State1},{dummy1_h,id,State2}] = lists:sort(Result3),
  980
+    ok = sys:resume(Pid),
977 981
     ok = gen_event:stop(Pid),
978 982
     ok.
979 983
 
@@ -998,4 +1002,11 @@ replace_state(Config) when is_list(Config) ->
998 1002
     Replace3 = fun(_) -> exit(fail) end,
999 1003
     [{dummy1_h,false,NState2}] = sys:replace_state(Pid, Replace3),
1000 1004
     [{dummy1_h,false,NState2}] = sys:get_state(Pid),
  1005
+    %% verify state replaced if process sys suspended
  1006
+    NState3 = "replaced again and again",
  1007
+    Replace4 = fun({dummy1_h,false,_}=S) -> setelement(3,S,NState3) end,
  1008
+    ok = sys:suspend(Pid),
  1009
+    [{dummy1_h,false,NState3}] = sys:replace_state(Pid, Replace4),
  1010
+    ok = sys:resume(Pid),
  1011
+    [{dummy1_h,false,NState3}] = sys:get_state(Pid),
1001 1012
     ok.
16  lib/stdlib/test/gen_fsm_SUITE.erl
@@ -426,6 +426,14 @@ get_state(Config) when is_list(Config) ->
426 426
     {idle, State} = sys:get_state(gfsm),
427 427
     {idle, State} = sys:get_state(gfsm, 5000),
428 428
     stop_it(Pid2),
  429
+
  430
+    %% check that get_state works when pid is sys suspended
  431
+    {ok, Pid3} = gen_fsm:start(gen_fsm_SUITE, {state_data, State}, []),
  432
+    {idle, State} = sys:get_state(Pid3),
  433
+    ok = sys:suspend(Pid3),
  434
+    {idle, State} = sys:get_state(Pid3, 5000),
  435
+    ok = sys:resume(Pid3),
  436
+    stop_it(Pid3),
429 437
     ok.
430 438
 
431 439
 replace_state(Config) when is_list(Config) ->
@@ -444,6 +452,14 @@ replace_state(Config) when is_list(Config) ->
444 452
     Replace3 = fun(_) -> error(fail) end,
445 453
     {state0, NState2} = sys:replace_state(Pid, Replace3),
446 454
     {state0, NState2} = sys:get_state(Pid),
  455
+    %% verify state replaced if process sys suspended
  456
+    ok = sys:suspend(Pid),
  457
+    Suffix2 = " and again",
  458
+    NState3 = NState2 ++ Suffix2,
  459
+    Replace3 = fun({StateName, _}) -> {StateName, NState3} end,
  460
+    {state0, NState3} = sys:replace_state(Pid, Replace3),
  461
+    ok = sys:resume(Pid),
  462
+    NState3 = sys:get_state(Pid, 5000),
447 463
     stop_it(Pid),
448 464
     ok.
449 465
 
11  lib/stdlib/test/gen_server_SUITE.erl
@@ -1049,6 +1049,9 @@ get_state(Config) when is_list(Config) ->
1049 1049
     {ok, Pid} = gen_server:start_link(?MODULE, {state,State}, []),
1050 1050
     State = sys:get_state(Pid),
1051 1051
     State = sys:get_state(Pid, 5000),
  1052
+    ok = sys:suspend(Pid),
  1053
+    State = sys:get_state(Pid),
  1054
+    ok = sys:resume(Pid),
1052 1055
     ok.
1053 1056
 
1054 1057
 %% Verify that sys:replace_state correctly replaces gen_server state
@@ -1077,6 +1080,14 @@ replace_state(Config) when is_list(Config) ->
1077 1080
     Replace3 = fun(_) -> throw(fail) end,
1078 1081
     NState2 = sys:replace_state(Pid, Replace3),
1079 1082
     NState2 = sys:get_state(Pid, 5000),
  1083
+    %% verify state replaced if process sys suspended
  1084
+    ok = sys:suspend(Pid),
  1085
+    Suffix2 = " and again",
  1086
+    NState3 = NState2 ++ Suffix2,
  1087
+    Replace3 = fun(S) -> S ++ Suffix2 end,
  1088
+    NState3 = sys:replace_state(Pid, Replace3),
  1089
+    ok = sys:resume(Pid),
  1090
+    NState3 = sys:get_state(Pid, 5000),
1080 1091
     ok.
1081 1092
 
1082 1093
 %% Test that the time for a huge message queue is not
38  system/doc/design_principles/spec_proc.xml
@@ -180,6 +180,18 @@ system_continue(Parent, Deb, Chs) ->
180 180
 system_terminate(Reason, Parent, Deb, Chs) ->
181 181
     exit(Reason).
182 182
 
  183
+system_get_state(Chs) ->
  184
+    {ok, Chs, Chs}.
  185
+
  186
+system_replace_state(StateFun, Chs) ->
  187
+    try StateFun(Chs) of
  188
+        Chs2 ->
  189
+            {ok, Chs2, Chs2}
  190
+    catch
  191
+        _:_ ->
  192
+            {ok, Chs, Chs}
  193
+    end.
  194
+
183 195
 write_debug(Dev, Event, Name) ->
184 196
     io:format(Dev, "~p event = ~p~n", [Name, Event]).</pre>
185 197
       <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>
353 365
         message and then call:</p>
354 366
       <code type="none">
355 367
 Module:system_continue(Parent, Deb, State)</code>
356  
-      <p>if process execution should continue, or:</p>
  368
+      <p>if process execution should continue, or if the process should
  369
+        terminate:</p>
357 370
       <code type="none">
358 371
 Module:system_terminate(Reason, Parent, Deb, State)</code>
359 372
       <p>if the process should terminate. Note that a process in a
360 373
         supervision tree is expected to terminate with the same reason as
361 374
         its parent.</p>
  375
+      <p>If the process should return its state sysHandle_system_msg will call:</p>
  376
+      <code type="none">
  377
+Module:system_get_state(State)</code>
  378
+      <p>Or if the process should replace its state using the fun <c>StateFun</c>:</p>
  379
+      <code type="none">
  380
+Module:system_replace_state(StateFun, State)</code>
362 381
       <list type="bulleted">
363 382
         <item><c>Request</c> and <c>From</c> should be passed as-is from
364 383
          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>
366 385
         <item><c>Module</c> is the name of the module.</item>
367 386
         <item><c>Deb</c> is the debug structure.</item>
368 387
         <item><c>State</c> is a term describing the internal state and
369  
-         is passed to <c>system_continue</c>/<c>system_terminate</c>.</item>
  388
+         is passed to <c>system_continue</c>/<c>system_terminate</c>/
  389
+         <c>system_get_state</c>/<c>system_replace_state</c>.</item>
370 390
       </list>
371 391
       <p>In the example:</p>
372 392
       <code type="none">
@@ -383,7 +403,19 @@ system_continue(Parent, Deb, Chs) ->
383 403
     loop(Chs, Parent, Deb).
384 404
 
385 405
 system_terminate(Reason, Parent, Deb, Chs) ->
386  
-    exit(Reason).</code>
  406
+    exit(Reason).
  407
+
  408
+system_get_state(Chs) ->
  409
+    {ok, Chs, Chs}.
  410
+
  411
+system_replace_state(StateFun, Chs) ->
  412
+    try StateFun(Chs) of
  413
+        Chs2 ->
  414
+            {ok, Chs2, Chs2}
  415
+    catch
  416
+        _:_ ->
  417
+            {ok, Chs, Chs}
  418
+    end.</code>
387 419
       <p>If the special process is set to trap exits, note that if
388 420
         the parent process terminates, the expected behavior is to
389 421
         terminate with the same reason:</p>

No commit comments for this range

Something went wrong with that request. Please try again.