diff options
Diffstat (limited to 'lib/stdlib')
| -rw-r--r-- | lib/stdlib/doc/src/gen_statem.xml | 247 | ||||
| -rw-r--r-- | lib/stdlib/src/eval_bits.erl | 14 | ||||
| -rw-r--r-- | lib/stdlib/src/gen_statem.erl | 260 | ||||
| -rw-r--r-- | lib/stdlib/test/gen_statem_SUITE.erl | 121 | 
4 files changed, 360 insertions, 282 deletions
| diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index ed44eef912..3322571b2c 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -97,6 +97,9 @@ gen_statem module            Callback module  gen_statem:start  gen_statem:start_link -----> Module:init/1 +Server start or code change +                      -----> Module:callback_mode/0 +  gen_statem:stop       -----> Module:terminate/3  gen_statem:call @@ -116,9 +119,11 @@ erlang:'!'            -----> Module:StateName/3      </p>      <p>        If a callback function fails or returns a bad value, -      the <c>gen_statem</c> terminates. However, an exception of class +      the <c>gen_statem</c> terminates, unless otherwise stated. +      However, an exception of class        <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso> -      is not regarded as an error but as a valid return. +      is not regarded as an error but as a valid return +      from all callback functions.      </p>      <marker id="state_function"/>      <p> @@ -127,7 +132,8 @@ erlang:'!'            -----> Module:StateName/3        in a <c>gen_statem</c> is the callback function that is called        for all events in this state. It is selected depending on which        <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> -      that the implementation specifies when the server starts. +      that the callback module defines with the callback function +      <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.      </p>      <p>        When the @@ -138,9 +144,9 @@ erlang:'!'            -----> Module:StateName/3        This gathers all code for a specific state        in one function as the <c>gen_statem</c> engine        branches depending on state name. -      Notice that in this mode the mandatory callback function +      Notice the fact that there is a mandatory callback function        <seealso marker="#Module:terminate/3"><c>Module:terminate/3</c></seealso> -      makes the state name <c>terminate</c> unusable. +      makes the state name <c>terminate</c> unusable in this mode.      </p>      <p>        When the @@ -249,11 +255,10 @@ erlang:'!'            -----> Module:StateName/3  -behaviour(gen_statem).  -export([start/0,push/0,get_count/0,stop/0]). --export([terminate/3,code_change/4,init/1]). +-export([terminate/3,code_change/4,init/1,callback_mode/0]).  -export([on/3,off/3]).  name() -> pushbutton_statem. % The registered server name -callback_mode() -> state_functions.  %% API.  This example uses a registered name name()  %% and does not link to the caller. @@ -270,15 +275,14 @@ stop() ->  terminate(_Reason, _State, _Data) ->      void.  code_change(_Vsn, State, Data, _Extra) -> -    {callback_mode(),State,Data}. +    {ok,State,Data}.  init([]) -> -    %% Set the callback mode and initial state + data. -    %% Data is used only as a counter. +    %% Set the initial state + data.  Data is used only as a counter.      State = off, Data = 0, -    {callback_mode(),State,Data}. - +    {ok,State,Data}. +callback_mode() -> state_functions. -%%% State functions +%%% State function(s)  off({call,From}, push, Data) ->      %% Go to 'on', increment count and reply @@ -326,18 +330,13 @@ ok        To compare styles, here follows the same example using        <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>        <c>state_functions</c>, or rather the code to replace -      from function <c>init/1</c> of the <c>pushbutton.erl</c> +      after function <c>init/1</c> of the <c>pushbutton.erl</c>        example file above:      </p>      <code type="erl"> -init([]) -> -    %% Set the callback mode and initial state + data. -    %% Data is used only as a counter. -    State = off, Data = 0, -    {handle_event_function,State,Data}. - +callback_mode() -> handle_event_function. -%%% Event handling +%%% State function(s)  handle_event({call,From}, push, off, Data) ->      %% Go to 'on', increment count and reply @@ -426,8 +425,8 @@ handle_event(_, _, State, Data) ->        <desc>  	<p>  	  Debug option that can be used when starting -	  a <c>gen_statem</c> server through, for example, -	  <seealso marker="#enter_loop/5"><c>enter_loop/5</c></seealso>. +	  a <c>gen_statem</c> server through, +	  <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>.  	</p>  	<p>  	  For every entry in <c><anno>Dbgs</anno></c>, @@ -525,12 +524,9 @@ handle_event(_, _, State, Data) ->        <desc>  	<p>  	  The <em>callback mode</em> is selected when starting the -	  <c>gen_statem</c> using the return value from -	  <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> -	  or when calling -	  <seealso marker="#enter_loop/5"><c>enter_loop/5,6,7</c></seealso>, -	  and with the return value from -	  <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso>. +	  <c>gen_statem</c> and after code change +	  using the return value from +	  <seealso marker="#Module:callback_mode/0"><c>Module:callback_mode/0</c></seealso>.  	</p>  	<taglist>  	  <tag><c>state_functions</c></tag> @@ -691,7 +687,7 @@ handle_event(_, _, State, Data) ->  	  <seealso marker="#state_function">state function</seealso>, from  	  <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>  	  or by giving them to -	  <seealso marker="#enter_loop/6"><c>enter_loop/6,7</c></seealso>. +	  <seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.  	</p>  	<p>  	  Actions are executed in the containing list order. @@ -923,7 +919,8 @@ handle_event(_, _, State, Data) ->  	</p>  	<note>  	  <p> -	    To avoid getting a late reply in the caller's +	    For <c><anno>Timeout</anno> =/= infinity</c>, +	    to avoid getting a late reply in the caller's  	    inbox, this function spawns a proxy process that  	    does the call. A late reply gets delivered to the  	    dead proxy process, hence gets discarded. This is @@ -958,35 +955,36 @@ handle_event(_, _, State, Data) ->      </func>      <func> -      <name name="enter_loop" arity="5"/> +      <name name="enter_loop" arity="4"/>        <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>        <desc>  	<p>  	  The same as -	  <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso> -	  except that no +	  <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso> +	  with <c>Actions = []</c> except that no  	  <seealso marker="#type-server_name"><c>server_name()</c></seealso> -	  must have been registered. +	  must have been registered.  This creates an anonymous server.  	</p>        </desc>      </func>      <func> -      <name name="enter_loop" arity="6"/> +      <name name="enter_loop" arity="5"/>        <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>        <desc>  	<p>  	  If <c><anno>Server_or_Actions</anno></c> is a <c>list()</c>,  	  the same as -	  <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso> +	  <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>  	  except that no  	  <seealso marker="#type-server_name"><c>server_name()</c></seealso>  	  must have been registered and  	  <c>Actions = <anno>Server_or_Actions</anno></c>. +	  This creates an anonymous server.  	</p>  	<p>  	  Otherwise the same as -	  <seealso marker="#enter_loop/7"><c>enter_loop/7</c></seealso> +	  <seealso marker="#enter_loop/6"><c>enter_loop/6</c></seealso>  	  with  	  <c>Server = <anno>Server_or_Actions</anno></c> and  	  <c>Actions = []</c>. @@ -995,7 +993,7 @@ handle_event(_, _, State, Data) ->      </func>      <func> -      <name name="enter_loop" arity="7"/> +      <name name="enter_loop" arity="6"/>        <fsummary>Enter the <c>gen_statem</c> receive loop.</fsummary>        <desc>  	<p> @@ -1015,21 +1013,31 @@ handle_event(_, _, State, Data) ->  	  the <c>gen_statem</c> behavior provides.  	</p>  	<p> -	  <c><anno>Module</anno></c>, <c><anno>Opts</anno></c>, and -	  <c><anno>Server</anno></c> have the same meanings -	  as when calling +	  <c><anno>Module</anno></c>, <c><anno>Opts</anno></c> +	  have the same meaning as when calling  	  <seealso marker="#start_link/3"><c>start[_link]/3,4</c></seealso>. +	</p> +	<p> +	  If <c><anno>Server</anno></c> is <c>self()</c> an anonymous +	  server is created just as when using  +	  <seealso marker="#start_link/3"><c>start[_link]/3</c></seealso>. +	  If <c><anno>Server</anno></c> is a +	  <seealso marker="#type-server_name"><c>server_name()</c></seealso> +	  a named server is created just as when using +	  <seealso marker="#start_link/4"><c>start[_link]/4</c></seealso>.  	  However, the  	  <seealso marker="#type-server_name"><c>server_name()</c></seealso>  	  name must have been registered accordingly -	  <em>before</em> this function is called.</p> +	  <em>before</em> this function is called. +	</p>          <p> -	  <c><anno>CallbackMode</anno></c>, <c><anno>State</anno></c>, -	  <c><anno>Data</anno></c>, and <c><anno>Actions</anno></c> +	  <c><anno>State</anno></c>, <c><anno>Data</anno></c>, +	  and <c><anno>Actions</anno></c>  	  have the same meanings as in the return value of  	  <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>. -	  Also, the callback module <c><anno>Module</anno></c> -	  does not need to export an <c>init/1</c> function. +	  Also, the callback module does not need to export a +	  <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> +	  function.  	</p>          <p>  	  The function fails if the calling process was not started by a @@ -1253,6 +1261,48 @@ handle_event(_, _, State, Data) ->    <funcs>      <func> +      <name>Module:callback_mode() -> CallbackMode</name> +      <fsummary>Update the internal state during upgrade/downgrade.</fsummary> +      <type> +	<v> +	  CallbackMode = +	  <seealso marker="#type-callback_mode">callback_mode()</seealso> +	</v> +      </type> +      <desc> +        <p> +	  This function is called by a <c>gen_statem</c> +	  when it needs to find out the +	  <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> +	  of the callback module.  The value is cached by <c>gen_statem</c> +	  for efficiency reasons, so this function is only called +	  once after server start and after code change, +	  but before the first +	  <seealso marker="#state_function">state function</seealso> +	  is called.  More occasions may be added in future versions +	  of <c>gen_statem</c>. +	</p> +	<p> +	  Server start happens either when +	  <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> +	  returns or when +	  <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso> +	  is called.  Code change happens when +	  <seealso marker="#Module:code_change/4"><c>Module:code_change/4</c></seealso> +	  returns. +	</p> +	<note> +	  <p> +	    If this function's body does not consist of solely one of two +	    possible +	    <seealso marker="#type-callback_mode">atoms</seealso> +	    the callback module is doing something strange. +	  </p> +	</note> +      </desc> +    </func> + +    <func>        <name>Module:code_change(OldVsn, OldState, OldData, Extra) ->          Result        </name> @@ -1262,11 +1312,7 @@ handle_event(_, _, State, Data) ->          <v>  Vsn = term()</v>          <v>OldState = NewState = term()</v>          <v>Extra = term()</v> -	<v>Result = {CallbackMode,NewState,NewData} | Reason</v> -	<v> -	  CallbackMode = -	  <seealso marker="#type-callback_mode">callback_mode()</seealso> -	</v> +	<v>Result = {ok,NewState,NewData} | Reason</v>  	<v>  	  OldState = NewState =  	  <seealso marker="#type-state">state()</seealso> @@ -1295,21 +1341,6 @@ handle_event(_, _, State, Data) ->  	  <c>Module</c>. If no such attribute is defined, the version  	  is the checksum of the Beam file.  	</p> -	<note> -	  <p> -	    If you would dare to change -	    <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> -	    during release upgrade/downgrade, the upgrade is no problem, -	    as the new code surely knows what <em>callback mode</em> -	    it needs. However, for a downgrade this function must -	    know from argument <c>Extra</c> that comes from the -	    <seealso marker="sasl:appup"><c>sasl:appup</c></seealso> -	    file what <em>callback mode</em> the old code did use. -	    It can also be possible to figure this out -	    from argument <c>{down,Vsn}</c>, as <c>Vsn</c> -	    in effect defines the old callback module version. -	  </p> -	</note>          <p>  	  <c>OldState</c> and <c>OldData</c> is the internal state  	  of the <c>gen_statem</c>. @@ -1321,39 +1352,32 @@ handle_event(_, _, State, Data) ->  	<p>  	  If successful, the function must return the updated  	  internal state in an -	  <c>{CallbackMode,NewState,NewData}</c> tuple. +	  <c>{ok,NewState,NewData}</c> tuple.  	</p>  	<p>  	  If the function returns a failure <c>Reason</c>, the ongoing  	  upgrade fails and rolls back to the old release. -	  Note that <c>Reason</c> can not be a 3-tuple since that -	  will be regarded as a -	  <c>{CallbackMode,NewState,NewData}</c> tuple, +	  Note that <c>Reason</c> can not be an <c>{ok,_,_}</c> tuple +	  since that will be regarded as a +	  <c>{ok,NewState,NewData}</c> tuple,  	  and that a tuple matching <c>{ok,_}</c> -	  is an invalid failure <c>Reason</c>. +	  is an also invalid failure <c>Reason</c>.  	  It is recommended to use an atom as <c>Reason</c> since  	  it will be wrapped in an <c>{error,Reason}</c> tuple.  	</p> -	<p> -	  This function can use -	  <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> -	  to return <c>Result</c> or <c>Reason</c>. -	</p>        </desc>      </func>      <func>        <name>Module:init(Args) -> Result</name> -      <fsummary>Initialize process and internal state.</fsummary> +      <fsummary> +	Optional function for initializing process and internal state. +      </fsummary>        <type>          <v>Args = term()</v> -        <v>Result = {CallbackMode,State,Data}</v> -	<v> | {CallbackMode,State,Data,Actions}</v> +        <v>Result = {ok,State,Data}</v> +	<v> | {ok,State,Data,Actions}</v>          <v> | {stop,Reason} | ignore</v> -	<v> -	  CallbackMode = -	  <seealso marker="#type-callback_mode">callback_mode()</seealso> -	</v>  	<v>State = <seealso marker="#type-state">state()</seealso></v>  	<v>  	  Data = <seealso marker="#type-data">data()</seealso> @@ -1372,7 +1396,7 @@ handle_event(_, _, State, Data) ->  	  <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>  	  or  	  <seealso marker="#start/3"><c>start/3,4</c></seealso>, -	  this function is called by the new process to initialize +	  this optional function is called by the new process to initialize  	  the implementation state and server data.  	</p>  	<p> @@ -1381,11 +1405,8 @@ handle_event(_, _, State, Data) ->  	</p>  	<p>  	  If the initialization is successful, the function is to -	  return <c>{CallbackMode,State,Data}</c> or -	  <c>{CallbackMode,State,Data,Actions}</c>. -	  <c>CallbackMode</c> selects the -	  <seealso marker="#type-callback_mode"><em>callback mode</em></seealso> -	  of the <c>gen_statem</c>. +	  return <c>{ok,State,Data}</c> or +	  <c>{ok,State,Data,Actions}</c>.  	  <c>State</c> is the initial  	  <seealso marker="#type-state"><c>state()</c></seealso>  	  and <c>Data</c> the initial server @@ -1403,11 +1424,16 @@ handle_event(_, _, State, Data) ->  	  or <c>ignore</c>; see            <seealso marker="#start_link/3"><c>start_link/3,4</c></seealso>.  	</p> -	<p> -	  This function can use -	  <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> -	  to return <c>Result</c>. -	</p> +        <note> +	  <p> +	    This callback is optional, so a callback module does not need +	    to export it, but most do.  If this function is not exported, +	    the <c>gen_statem</c> should be started through +	    <seealso marker="proc_lib"><c>proc_lib</c></seealso> +	    and +	    <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>. +	  </p> +        </note>        </desc>      </func> @@ -1438,10 +1464,14 @@ handle_event(_, _, State, Data) ->  	    This callback is optional, so a callback module does not need  	    to export it. The <c>gen_statem</c> module provides a default  	    implementation of this function that returns -	    <c>{State,Data}</c>. If this callback fails, the default -	    function returns <c>{State,Info}</c>, -	    where <c>Info</c> informs of the crash but no details, -	    to hide possibly sensitive data. +	    <c>{State,Data}</c>. +	  </p> +	  <p> +	    If this callback is exported but fails, +	    to hide possibly sensitive data, +	    the default function will instead return <c>{State,Info}</c>, +	    where <c>Info</c> says nothing but the fact that +	    <c>format_status/2</c> has crashed.  	  </p>          </note>          <p>This function is called by a <c>gen_statem</c> process when @@ -1502,11 +1532,6 @@ handle_event(_, _, State, Data) ->            printed in log files. Another use is to hide sensitive data from  	  being written to the error log.  	</p> -	<p> -	  This function can use -	  <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> -	  to return <c>Status</c>. -	</p>        </desc>      </func> @@ -1581,9 +1606,12 @@ handle_event(_, _, State, Data) ->  	  see <seealso marker="#type-action"><c>action()</c></seealso>.  	</p>  	<p> -	  These functions can use -	  <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso>, -	  to return the result. +	  Note the fact that you can use +	  <seealso marker="erts:erlang#throw/1"><c>throw</c></seealso> +	  to return the result, which can be useful. +	  For example to bail out with <c>throw(keep_state_and_data)</c> +	  from deep within complex code that is in no position to +	  return <c>{next_state,State,Data}</c>.  	</p>        </desc>      </func> @@ -1656,11 +1684,6 @@ handle_event(_, _, State, Data) ->  	  and an error report is issued using            <seealso marker="kernel:error_logger#format/2"><c>error_logger:format/2</c></seealso>.  	</p> -	<p> -	  This function can use -	  <seealso marker="erts:erlang#throw/1"><c>erlang:throw/1</c></seealso> -	  to return <c>Ignored</c>, which is ignored anyway. -	</p>        </desc>      </func>    </funcs> diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl index 80667023fb..631faa3be5 100644 --- a/lib/stdlib/src/eval_bits.erl +++ b/lib/stdlib/src/eval_bits.erl @@ -67,16 +67,20 @@ expr_grp([Field | FS], Bs0, Lf, Acc) ->  expr_grp([], Bs0, _Lf, Acc) ->      {value,Acc,Bs0}. +eval_field({bin_element, _, {string, _, S}, {integer,_,8}, [integer,{unit,1},unsigned,big]}, Bs0, _Fun) -> +    Latin1 = [C band 16#FF || C <- S], +    {list_to_binary(Latin1),Bs0};  eval_field({bin_element, _, {string, _, S}, default, default}, Bs0, _Fun) ->      Latin1 = [C band 16#FF || C <- S],      {list_to_binary(Latin1),Bs0}; -eval_field({bin_element, Line, {string, _, S}, Size0, Options0}, Bs, _Fun) -> -    {_Size,[Type,_Unit,_Sign,Endian]} =  +eval_field({bin_element, Line, {string, _, S}, Size0, Options0}, Bs0, Fun) -> +    {Size1,[Type,{unit,Unit},Sign,Endian]} =          make_bit_type(Line, Size0, Options0), -    Res = << <<(eval_exp_field1(C, no_size, no_unit, -				Type, Endian, no_sign))/binary>> || +    {value,Size,Bs1} = Fun(Size1, Bs0), +    Res = << <<(eval_exp_field1(C, Size, Unit, +				Type, Endian, Sign))/binary>> ||  	      C <- S >>, -    {Res,Bs}; +    {Res,Bs1};  eval_field({bin_element,Line,E,Size0,Options0}, Bs0, Fun) ->      {value,V,Bs1} = Fun(E, Bs0),      {Size1,[Type,{unit,Unit},Sign,Endian]} =  diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index c02e6a1a19..3b3477b282 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -24,7 +24,7 @@     [start/3,start/4,start_link/3,start_link/4,      stop/1,stop/3,      cast/2,call/2,call/3, -    enter_loop/5,enter_loop/6,enter_loop/7, +    enter_loop/4,enter_loop/5,enter_loop/6,      reply/1,reply/2]).  %% gen callbacks @@ -63,8 +63,8 @@  	{To :: pid(), Tag :: term()}. % Reply-to specifier for call  -type state() :: -	state_name() | % For state callback function StateName/5 -	term(). % For state callback function handle_event/5 +	state_name() | % For StateName/3 callback functios +	term(). % For handle_event/4 callback function  -type state_name() :: atom(). @@ -174,28 +174,33 @@  %% an {ok, ...} tuple.  Thereafter the state callbacks are called  %% for all events to this server.  -callback init(Args :: term()) -> -    {callback_mode(), state(), data()} | -    {callback_mode(), state(), data(), [action()] | action()} | +    {ok, state(), data()} | +    {ok, state(), data(), [action()] | action()} |      'ignore' |      {'stop', Reason :: term()}. -%% Example state callback for callback_mode() =:= state_functions -%% state name 'state_name'. +%% This callback shall return the callback mode of the callback module.  %% -%% In this mode all states has to be type state_name() i.e atom(). +%% It is called once after init/0 and code_change/4 but before +%% the first state callback StateName/3 or handle_event/4. +-callback callback_mode() -> callback_mode(). + +%% Example state callback for StateName = 'state_name' +%% when callback_mode() =:= state_functions. +%% +%% In this mode all states has to be of type state_name() i.e atom().  %% -%% Note that state callbacks and only state callbacks have arity 5 -%% and that is intended. +%% Note that the only callbacks that have arity 3 are these +%% StateName/3 callbacks and terminate/3, so the state name +%% 'terminate' is unusable in this mode.  -callback state_name(  	    event_type(),  	    EventContent :: term(),  	    Data :: data()) ->      state_function_result().  %% -%% State callback for callback_mode() =:= handle_event_function. -%% -%% Note that state callbacks and only state callbacks have arity 5 -%% and that is intended. +%% State callback for all states +%% when callback_mode() =:= handle_event_function.  -callback handle_event(  	    event_type(),  	    EventContent :: term(), @@ -219,9 +224,7 @@  	    OldState :: state(),  	    OldData :: data(),  	    Extra :: term()) -> -    {CallbackMode :: callback_mode(), -     NewState :: state(), -     NewData :: data()} | +    {ok, NewState :: state(), NewData :: data()} |      (Reason :: term()).  %% Format the callback module state in some sensible that is @@ -240,10 +243,13 @@     [init/1, % One may use enter_loop/5,6,7 instead      format_status/2, % Has got a default implementation      %% -    state_name/3, % Example for callback_mode =:= state_functions: -    %% there has to be a StateName/5 callback function for every StateName. +    state_name/3, % Example for callback_mode() =:= state_functions: +    %% there has to be a StateName/3 callback function +    %% for every StateName in your state machine but the state name +    %% 'state_name' does of course not have to be used.      %% -    handle_event/4]). % For callback_mode =:= handle_event_function +    handle_event/4 % For callback_mode() =:= handle_event_function +   ]).  %% Type validation functions  callback_mode(CallbackMode) -> @@ -451,43 +457,35 @@ reply({To,Tag}, Reply) when is_pid(To) ->  %% the same arguments as you would have returned from init/1  -spec enter_loop(  	Module :: module(), Opts :: [debug_opt()], -	CallbackMode :: callback_mode(),  	State :: state(), Data :: data()) ->  			no_return(). -enter_loop(Module, Opts, CallbackMode, State, Data) -> -    enter_loop(Module, Opts, CallbackMode, State, Data, self()). +enter_loop(Module, Opts, State, Data) -> +    enter_loop(Module, Opts, State, Data, self()).  %%  -spec enter_loop(  	Module :: module(), Opts :: [debug_opt()], -	CallbackMode :: callback_mode(),  	State :: state(), Data :: data(),  	Server_or_Actions ::  	  server_name() | pid() | [action()]) ->  			no_return(). -enter_loop(Module, Opts, CallbackMode, State, Data, Server_or_Actions) -> +enter_loop(Module, Opts, State, Data, Server_or_Actions) ->      if  	is_list(Server_or_Actions) -> -	    enter_loop( -	      Module, Opts, CallbackMode, State, Data, -	      self(), Server_or_Actions); +	    enter_loop(Module, Opts, State, Data, self(), Server_or_Actions);  	true -> -	    enter_loop( -	      Module, Opts, CallbackMode, State, Data, -	      Server_or_Actions, []) +	    enter_loop(Module, Opts, State, Data, Server_or_Actions, [])      end.  %%  -spec enter_loop(  	Module :: module(), Opts :: [debug_opt()], -	CallbackMode :: callback_mode(),  	State :: state(), Data :: data(),  	Server :: server_name() | pid(),  	Actions :: [action()] | action()) ->  			no_return(). -enter_loop(Module, Opts, CallbackMode, State, Data, Server, Actions) -> +enter_loop(Module, Opts, State, Data, Server, Actions) ->      is_atom(Module) orelse error({atom,Module}), -    callback_mode(CallbackMode) orelse error({callback_mode,CallbackMode}),      Parent = gen:get_parent(), -    enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent). +    enter(Module, Opts, State, Data, Server, Actions, Parent).  %%---------------------------------------------------------------------------  %% API helpers @@ -515,7 +513,7 @@ send(Proc, Msg) ->      end.  %% Here the init_it/6 and enter_loop/5,6,7 functions converge -enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) -> +enter(Module, Opts, State, Data, Server, Actions, Parent) ->      %% The values should already have been type checked      Name = gen:get_proc_name(Server),      Debug = gen:debug_options(Name, Opts), @@ -531,7 +529,7 @@ enter(Module, Opts, CallbackMode, State, Data, Server, Actions, Parent) ->  		[Actions,{postpone,false}]  	end,      S =	#{ -      callback_mode => CallbackMode, +      callback_mode => undefined,        module => Module,        name => Name,        %% All fields below will be replaced according to the arguments to @@ -559,9 +557,15 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->  	Result ->  	    init_result(Starter, Parent, ServerRef, Module, Result, Opts);  	Class:Reason -> +	    Stacktrace = erlang:get_stacktrace(), +	    Name = gen:get_proc_name(ServerRef),  	    gen:unregister_name(ServerRef),  	    proc_lib:init_ack(Starter, {error,Reason}), -	    erlang:raise(Class, Reason, erlang:get_stacktrace()) +	    error_info( +	      Class, Reason, Stacktrace, +	      #{name => Name, callback_mode => undefined}, +	      [], [], undefined), +	    erlang:raise(Class, Reason, Stacktrace)      end.  %%--------------------------------------------------------------------------- @@ -569,30 +573,12 @@ init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->  init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->      case Result of -	{CallbackMode,State,Data} -> -	    case callback_mode(CallbackMode) of -		true -> -		    proc_lib:init_ack(Starter, {ok,self()}), -		    enter( -		      Module, Opts, CallbackMode, State, Data, -		      ServerRef, [], Parent); -		false -> -		    Error = {callback_mode,CallbackMode}, -		    proc_lib:init_ack(Starter, {error,Error}), -		    exit(Error) -	    end; -	{CallbackMode,State,Data,Actions} -> -	    case callback_mode(CallbackMode) of -		true -> -		    proc_lib:init_ack(Starter, {ok,self()}), -		    enter( -		      Module, Opts, CallbackMode, State, Data, -		      ServerRef, Actions, Parent); -		false -> -		    Error = {callback_mode,CallbackMode}, -		    proc_lib:init_ack(Starter, {error,Error}), -		    exit(Error) -	    end; +	{ok,State,Data} -> +	    proc_lib:init_ack(Starter, {ok,self()}), +	    enter(Module, Opts, State, Data, ServerRef, [], Parent); +	{ok,State,Data,Actions} -> +	    proc_lib:init_ack(Starter, {ok,self()}), +	    enter(Module, Opts, State, Data, ServerRef, Actions, Parent);  	{stop,Reason} ->  	    gen:unregister_name(ServerRef),  	    proc_lib:init_ack(Starter, {error,Reason}), @@ -602,8 +588,14 @@ init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->  	    proc_lib:init_ack(Starter, ignore),  	    exit(normal);  	_ -> -	    Error = {bad_return_value,Result}, +	    Name = gen:get_proc_name(ServerRef), +	    gen:unregister_name(ServerRef), +	    Error = {bad_return_from_init,Result},  	    proc_lib:init_ack(Starter, {error,Error}), +	    error_info( +	      error, Error, ?STACKTRACE(), +	      #{name => Name, callback_mode => undefined}, +	      [], [], undefined),  	    exit(Error)      end. @@ -631,11 +623,9 @@ system_code_change(  	    Result -> Result  	end      of -	{CallbackMode,NewState,NewData} -> -	    callback_mode(CallbackMode) orelse -		error({callback_mode,CallbackMode}), +	{ok,NewState,NewData} ->  	    {ok, -	     S#{callback_mode := CallbackMode, +	     S#{callback_mode := undefined,  		state := NewState,  		data := NewData}};  	{ok,_} = Error -> @@ -676,14 +666,14 @@ format_status(  %% them, not as the real erlang messages.  Use trace for that.  %%--------------------------------------------------------------------------- -print_event(Dev, {in,Event}, {Name,_}) -> +print_event(Dev, {in,Event}, {Name,State}) ->      io:format( -      Dev, "*DBG* ~p received ~s~n", -      [Name,event_string(Event)]); -print_event(Dev, {out,Reply,{To,_Tag}}, {Name,_}) -> +      Dev, "*DBG* ~p receive ~s in state ~p~n", +      [Name,event_string(Event),State]); +print_event(Dev, {out,Reply,{To,_Tag}}, {Name,State}) ->      io:format( -      Dev, "*DBG* ~p sent ~p to ~p~n", -      [Name,Reply,To]); +      Dev, "*DBG* ~p send ~p to ~p from state ~p~n", +      [Name,Reply,To,State]);  print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->      StateString =  	case NextState of @@ -875,22 +865,36 @@ loop_event(      %%      try  	case CallbackMode of +	    undefined -> +		Module:callback_mode();  	    state_functions -> -		Module:State(Type, Content, Data); +		erlang:apply(Module, State, [Type,Content,Data]);  	    handle_event_function ->  		Module:handle_event(Type, Content, State, Data) -	end of +	end +    of +	Result when CallbackMode =:= undefined -> +	    loop_event_callback_mode( +	      Parent, Debug, S, Events, State, Data, P, Event, Result);  	Result ->  	    loop_event_result(  	      Parent, Debug, S, Events, State, Data, P, Event, Result)      catch +	Result when CallbackMode =:= undefined -> +	    loop_event_callback_mode( +	      Parent, Debug, S, Events, State, Data, P, Event, Result);  	Result ->  	    loop_event_result(  	      Parent, Debug, S, Events, State, Data, P, Event, Result); -	error:badarg when CallbackMode =:= state_functions -> +	error:badarg ->  	    case erlang:get_stacktrace() of -		[{erlang,apply,[Module,State,_],_}|Stacktrace] -> -		    Args = [Type,Content,Data], +		[{erlang,apply, +		  [Module,State,[Type,Content,Data]=Args], +		  _} +		 |Stacktrace] +		  when CallbackMode =:= state_functions -> +		    %% We get here e.g if apply fails +		    %% due to State not being an atom  		    terminate(  		      error,  		      {undef_state_function,{Module,State,Args}}, @@ -902,24 +906,29 @@ loop_event(  		      Debug, S, [Event|Events], State, Data, P)  	    end;  	error:undef -> -	    %% Process an undef to check for the simple mistake +	    %% Process undef to check for the simple mistake  	    %% of calling a nonexistent state function +	    %% to make the undef more precise  	    case erlang:get_stacktrace() of -		[{Module,State, -		  [Type,Content,Data]=Args, -		  _} +		[{Module,callback_mode,[]=Args,_} +		 |Stacktrace] +		  when CallbackMode =:= undefined -> +		    terminate( +		      error, +		      {undef_callback,{Module,callback_mode,Args}}, +		      Stacktrace, +		      Debug, S, [Event|Events], State, Data, P); +		[{Module,State,[Type,Content,Data]=Args,_}  		 |Stacktrace] -		when CallbackMode =:= state_functions -> +		  when CallbackMode =:= state_functions ->  		    terminate(  		      error,  		      {undef_state_function,{Module,State,Args}},  		      Stacktrace,  		      Debug, S, [Event|Events], State, Data, P); -		[{Module,handle_event, -		  [Type,Content,State,Data]=Args, -		  _} +		[{Module,handle_event,[Type,Content,State,Data]=Args,_}  		 |Stacktrace] -		when CallbackMode =:= handle_event_function -> +		  when CallbackMode =:= handle_event_function ->  		    terminate(  		      error,  		      {undef_state_function,{Module,handle_event,Args}}, @@ -937,6 +946,25 @@ loop_event(  	      Debug, S, [Event|Events], State, Data, P)      end. +%% Interpret callback_mode() result +loop_event_callback_mode( +  Parent, Debug, S, Events, State, Data, P, Event, CallbackMode) -> +    case callback_mode(CallbackMode) of +	true -> +	    Hibernate = false, % We have already GC:ed recently +	    loop_event( +	      Parent, Debug, +	      S#{callback_mode := CallbackMode}, +	      Events, +	      State, Data, P, Event, Hibernate); +	false -> +	    terminate( +	      error, +	      {bad_return_from_callback_mode,CallbackMode}, +	      ?STACKTRACE(), +	      Debug, S, [Event|Events], State, Data, P) +    end. +  %% Interpret all callback return variants  loop_event_result(    Parent, Debug, S, Events, State, Data, P, Event, Result) -> @@ -989,7 +1017,9 @@ loop_event_result(  	      State, Data, P, Event, State, Actions);  	_ ->  	    terminate( -	      error, {bad_return_value,Result}, ?STACKTRACE(), +	      error, +	      {bad_return_from_state_function,Result}, +	      ?STACKTRACE(),  	      Debug, S, [Event|Events], State, Data, P)      end. @@ -1026,20 +1056,26 @@ loop_event_actions(  		      Postpone, Hibernate, Timeout, NextEvents);  		false ->  		    terminate( -		      error, {bad_action,Action}, ?STACKTRACE(), +		      error, +		      {bad_action_from_state_function,Action}, +		      ?STACKTRACE(),  		      Debug, S, [Event|Events], State, NewData, P)  	    end;  	{next_event,Type,Content} ->  	    case event_type(Type) of  		true -> +		    NewDebug = +			sys_debug(Debug, S, State, {in,{Type,Content}}),  		    loop_event_actions( -		      Parent, Debug, S, Events, +		      Parent, NewDebug, S, Events,  		      State, NewData, P, Event, NextState, Actions,  		      Postpone, Hibernate, Timeout,  		      [{Type,Content}|NextEvents]);  		false ->  		    terminate( -		      error, {bad_action,Action}, ?STACKTRACE(), +		      error, +		      {bad_action_from_state_function,Action}, +		      ?STACKTRACE(),  		      Debug, S, [Event|Events], State, NewData, P)  	    end;  	%% Actions that set options @@ -1050,7 +1086,9 @@ loop_event_actions(  	      NewPostpone, Hibernate, Timeout, NextEvents);  	{postpone,_} ->  	    terminate( -	      error, {bad_action,Action}, ?STACKTRACE(), +	      error, +	      {bad_action_from_state_function,Action}, +	      ?STACKTRACE(),  	      Debug, S, [Event|Events], State, NewData, P);  	postpone ->  	    loop_event_actions( @@ -1064,7 +1102,9 @@ loop_event_actions(  	      Postpone, NewHibernate, Timeout, NextEvents);  	{hibernate,_} ->  	    terminate( -	      error, {bad_action,Action}, ?STACKTRACE(), +	      error, +	      {bad_action_from_state_function,Action}, +	      ?STACKTRACE(),  	      Debug, S, [Event|Events], State, NewData, P);  	hibernate ->  	    loop_event_actions( @@ -1083,7 +1123,9 @@ loop_event_actions(  	      Postpone, Hibernate, NewTimeout, NextEvents);  	{timeout,_,_} ->  	    terminate( -	      error, {bad_action,Action}, ?STACKTRACE(), +	      error, +	      {bad_action_from_state_function,Action}, +	      ?STACKTRACE(),  	      Debug, S, [Event|Events], State, NewData, P);  	infinity -> % Clear timer - it will never trigger  	    loop_event_actions( @@ -1098,7 +1140,9 @@ loop_event_actions(  	      Postpone, Hibernate, NewTimeout, NextEvents);  	_ ->  	    terminate( -	      error, {bad_action,Action}, ?STACKTRACE(), +	      error, +	      {bad_action_from_state_function,Action}, +	      ?STACKTRACE(),  	      Debug, S, [Event|Events], State, NewData, P)      end;  %% @@ -1170,7 +1214,9 @@ do_reply_then_terminate(  	      NewDebug, S, Q, State, Data, P, Rs);  	_ ->  	    terminate( -	      error, {bad_action,R}, ?STACKTRACE(), +	      error, +	      {bad_reply_action_from_state_function,R}, +	      ?STACKTRACE(),  	      Debug, S, Q, State, Data, P)      end. @@ -1189,8 +1235,9 @@ terminate(  	C:R ->  	    ST = erlang:get_stacktrace(),  	    error_info( -	      C, R, ST, Debug, S, Q, P, +	      C, R, ST, S, Q, P,  	      format_status(terminate, get(), S, State, Data)), +	    sys:print_log(Debug),  	    erlang:raise(C, R, ST)      end,      case Reason of @@ -1199,8 +1246,9 @@ terminate(  	{shutdown,_} -> ok;  	_ ->  	    error_info( -	      Class, Reason, Stacktrace, Debug, S, Q, P, -	      format_status(terminate, get(), S, State, Data)) +	      Class, Reason, Stacktrace, S, Q, P, +	      format_status(terminate, get(), S, State, Data)), +	    sys:print_log(Debug)      end,      case Stacktrace of  	[] -> @@ -1210,7 +1258,7 @@ terminate(      end.  error_info( -  Class, Reason, Stacktrace, Debug, +  Class, Reason, Stacktrace,    #{name := Name, callback_mode := CallbackMode},    Q, P, FmtData) ->      {FixedReason,FixedStacktrace} = @@ -1277,9 +1325,7 @@ error_info(  	  case FixedStacktrace of  	      [] -> [];  	      _ -> [FixedStacktrace] -	  end), -    sys:print_log(Debug), -    ok. +	  end).  %% Call Module:format_status/2 or return a default value @@ -1292,7 +1338,7 @@ format_status(Opt, PDict, #{module := Module}, State, Data) ->  		_:_ ->  		    format_status_default(  		      Opt, State, -		      "Module:format_status/2 crashed") +		      atom_to_list(Module) ++ ":format_status/2 crashed")  	    end;  	false ->  	    format_status_default(Opt, State, Data) @@ -1300,10 +1346,10 @@ format_status(Opt, PDict, #{module := Module}, State, Data) ->  %% The default Module:format_status/2  format_status_default(Opt, State, Data) -> -    SSD = {State,Data}, +    StateData = {State,Data},      case Opt of  	terminate -> -	    SSD; +	    StateData;  	_ -> -	    [{data,[{"State",SSD}]}] +	    [{data,[{"State",StateData}]}]      end. diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 364314f91b..1d1417c2e6 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -37,33 +37,32 @@ all() ->       {group, stop_handle_event},       {group, abnormal},       {group, abnormal_handle_event}, -     shutdown, stop_and_reply, event_order, +     shutdown, stop_and_reply, event_order, code_change,       {group, sys},       hibernate, enter_loop].  groups() -> -    [{start, [], -      [start1, start2, start3, start4, start5, start6, start7, -       start8, start9, start10, start11, start12, next_events]}, -     {start_handle_event, [], -      [start1, start2, start3, start4, start5, start6, start7, -       start8, start9, start10, start11, start12, next_events]}, -     {stop, [], -      [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]}, -     {stop_handle_event, [], -      [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]}, -     {abnormal, [], [abnormal1, abnormal2]}, -     {abnormal_handle_event, [], [abnormal1, abnormal2]}, -     {sys, [], -      [sys1, code_change, -       call_format_status, -       error_format_status, terminate_crash_format, -       get_state, replace_state]}, -     {sys_handle_event, [], -      [sys1, -       call_format_status, -       error_format_status, terminate_crash_format, -       get_state, replace_state]}]. +    [{start, [], tcs(start)}, +     {start_handle_event, [], tcs(start)}, +     {stop, [], tcs(stop)}, +     {stop_handle_event, [], tcs(stop)}, +     {abnormal, [], tcs(abnormal)}, +     {abnormal_handle_event, [], tcs(abnormal)}, +     {sys, [], tcs(sys)}, +     {sys_handle_event, [], tcs(sys)}]. + +tcs(start) -> +    [start1, start2, start3, start4, start5, start6, start7, +     start8, start9, start10, start11, start12, next_events]; +tcs(stop) -> +    [stop1, stop2, stop3, stop4, stop5, stop6, stop7, stop8, stop9, stop10]; +tcs(abnormal) -> +    [abnormal1, abnormal2]; +tcs(sys) -> +    [sys1, call_format_status, +     error_format_status, terminate_crash_format, +     get_state, replace_state]. +  init_per_suite(Config) ->      Config. @@ -461,10 +460,10 @@ abnormal2(Config) ->      {ok,Pid} = gen_statem:start_link(?MODULE, start_arg(Config, []), []),      %% bad return value in the gen_statem loop -    {{bad_return_value,badreturn},_} = +    {{bad_return_from_state_function,badreturn},_} =  	?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),      receive -	{'EXIT',Pid,{bad_return_value,badreturn}} -> ok +	{'EXIT',Pid,{bad_return_from_state_function,badreturn}} -> ok      after 5000 ->  	    ct:fail(gen_statem_did_not_die)      end, @@ -633,11 +632,13 @@ sys1(Config) ->      sys:resume(Pid),      stop_it(Pid). -code_change(Config) -> -    Mode = handle_event_function, -    {ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []), +code_change(_Config) -> +    {ok,Pid} = +	gen_statem:start( +	  ?MODULE, {callback_mode,state_functions,[]}, []),      {idle,data} = sys:get_state(Pid),      sys:suspend(Pid), +    Mode = handle_event_function,      sys:change_code(Pid, ?MODULE, old_vsn, Mode),      sys:resume(Pid),      {idle,{old_vsn,data,Mode}} = sys:get_state(Pid), @@ -708,7 +709,7 @@ error_format_status(Config) ->  	gen_statem:start(  	  ?MODULE, start_arg(Config, {data,Data}), []),      %% bad return value in the gen_statem loop -    {{bad_return_value,badreturn},_} = +    {{bad_return_from_state_function,badreturn},_} =  	?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),      receive  	{error,_, @@ -716,7 +717,7 @@ error_format_status(Config) ->  	  "** State machine"++_,  	  [Pid,{{call,_},badreturn},  	   {formatted,idle,Data}, -	   error,{bad_return_value,badreturn}|_]}} -> +	   error,{bad_return_from_state_function,badreturn}|_]}} ->  	    ok;  	Other when is_tuple(Other), element(1, Other) =:= error ->  	    error_logger_forwarder:unregister(), @@ -1029,11 +1030,7 @@ enter_loop(_Config) ->      end,      %% Process not started using proc_lib -    CallbackMode = state_functions, -    Pid4 = -	spawn_link( -	  gen_statem, enter_loop, -	  [?MODULE,[],CallbackMode,state0,[]]), +    Pid4 = spawn_link(gen_statem, enter_loop, [?MODULE,[],state0,[]]),      receive  	{'EXIT',Pid4,process_was_not_started_by_proc_lib} ->  	    ok @@ -1107,21 +1104,18 @@ enter_loop(Reg1, Reg2) ->  	anon -> ignore      end,      proc_lib:init_ack({ok, self()}), -    CallbackMode = state_functions,      case Reg2 of  	local ->  	    gen_statem:enter_loop( -	      ?MODULE, [], CallbackMode, state0, [], {local,armitage}); +	      ?MODULE, [], state0, [], {local,armitage});  	global ->  	    gen_statem:enter_loop( -	      ?MODULE, [], CallbackMode, state0, [], {global,armitage}); +	      ?MODULE, [], state0, [], {global,armitage});  	via ->  	    gen_statem:enter_loop( -	      ?MODULE, [], CallbackMode, state0, [], -	      {via, dummy_via, armitage}); +	      ?MODULE, [], state0, [], {via, dummy_via, armitage});  	anon -> -	    gen_statem:enter_loop( -	      ?MODULE, [], CallbackMode, state0, []) +	    gen_statem:enter_loop(?MODULE, [], state0, [])      end. @@ -1266,33 +1260,39 @@ init(stop_shutdown) ->      {stop,shutdown};  init(sleep) ->      ?t:sleep(1000), -    {state_functions,idle,data}; +    {ok,idle,data};  init(hiber) -> -    {state_functions,hiber_idle,[]}; +    {ok,hiber_idle,[]};  init(hiber_now) -> -    {state_functions,hiber_idle,[],[hibernate]}; +    {ok,hiber_idle,[],[hibernate]};  init({data, Data}) -> -    {state_functions,idle,Data}; +    {ok,idle,Data};  init({callback_mode,CallbackMode,Arg}) -> -    case init(Arg) of -	{_,State,Data,Ops} -> -	    {CallbackMode,State,Data,Ops}; -	{_,State,Data} -> -	    {CallbackMode,State,Data}; -	Other -> -	    Other -    end; +    ets:new(?MODULE, [named_table,private]), +    ets:insert(?MODULE, {callback_mode,CallbackMode}), +    init(Arg);  init({map_statem,#{init := Init}=Machine}) -> +    ets:new(?MODULE, [named_table,private]), +    ets:insert(?MODULE, {callback_mode,handle_event_function}),      case Init() of  	{ok,State,Data,Ops} -> -	    {handle_event_function,State,[Data|Machine],Ops}; +	    {ok,State,[Data|Machine],Ops};  	{ok,State,Data} -> -	    {handle_event_function,State,[Data|Machine]}; +	    {ok,State,[Data|Machine]};  	Other ->  	    Other      end;  init([]) -> -    {state_functions,idle,data}. +    {ok,idle,data}. + +callback_mode() -> +    try ets:lookup(?MODULE, callback_mode) of +	[{callback_mode,CallbackMode}] -> +	    CallbackMode +    catch +	error:badarg -> +	    state_functions +    end.  terminate(_, _State, crash_terminate) ->      exit({crash,terminate}); @@ -1568,7 +1568,12 @@ wrap_result(Result) ->  code_change(OldVsn, State, Data, CallbackMode) -> -    {CallbackMode,State,{OldVsn,Data,CallbackMode}}. +    io:format( +      "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]), +    ets:insert(?MODULE, {callback_mode,CallbackMode}), +    io:format( +      "code_change(~p, ~p, ~p, ~p)~n", [OldVsn,State,Data,CallbackMode]), +    {ok,State,{OldVsn,Data,CallbackMode}}.  format_status(terminate, [_Pdict,State,Data]) ->      {formatted,State,Data}; | 
