Allow customizing the StateData in p1_fsm before error reporting.

A new callback is introduced on the p1_fsm behaviour:

print_error/1

If the module implements this function, it will be invoked
in case of process crash with the current state data *before*
printing the error in the log. The function must return the
desired State to print.

It is used in ejabberd_c2s to prune the presence sets that
can be large. Instead, the state is changed to include only
the # of elements on each set.

Change inspired in comming changes to gen_server on OTP, and
b01d15abc3 (diff-0)
This commit is contained in:
Pablo Polvorin 2010-03-30 16:39:34 -03:00
parent 44b282474a
commit 41aa693896
2 changed files with 28 additions and 8 deletions

View File

@ -55,7 +55,9 @@
handle_sync_event/4,
code_change/4,
handle_info/3,
terminate/3]).
terminate/3,
print_state/1
]).
-include("ejabberd.hrl").
-include("jlib.hrl").
@ -1334,6 +1336,19 @@ handle_info(Info, StateName, StateData) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
fsm_next_state(StateName, StateData).
%%----------------------------------------------------------------------
%% Func: print_state/1
%% Purpose: Prepare the state to be printed on error log
%% Returns: State to print
%%----------------------------------------------------------------------
print_state(State = #state{pres_t = T, pres_f = F, pres_a = A, pres_i = I}) ->
State#state{pres_t = {pres_t, ?SETS:size(T)},
pres_f = {pres_f, ?SETS:size(F)},
pres_a = {pres_a, ?SETS:size(A)},
pres_i = {pres_i, ?SETS:size(I)}
}.
%%----------------------------------------------------------------------
%% Func: terminate/3
%% Purpose: Shutdown the fsm

View File

@ -25,7 +25,8 @@
%% - You can limit the time processing a message (TODO): If the
%% message processing does not return in a given period of time, the
%% process will be terminated.
%%
%% - You might customize the State data before sending it to error_logger
%% in case of a crash (just export the function print_state/1)
%% $Id$
%%
-module(p1_fsm).
@ -146,7 +147,7 @@
behaviour_info(callbacks) ->
[{init,1},{handle_event,3},{handle_sync_event,4},{handle_info,3},
{terminate,3},{code_change,4}];
{terminate,3},{code_change,4}, {print_state,1}];
behaviour_info(_Other) ->
undefined.
@ -376,7 +377,7 @@ loop(Parent, Name, StateName, StateData, Mod, hibernate, Debug,
Debug, Limits, Queue1, QueueLen - 1, false);
{empty, _} ->
Reason = internal_queue_error,
error_info(Reason, Name, hibernate, StateName, StateData, Debug),
error_info(Mod, Reason, Name, hibernate, StateName, StateData, Debug),
exit(Reason)
end;
loop(Parent, Name, StateName, StateData, Mod, hibernate, Debug,
@ -620,7 +621,7 @@ reply(Name, {To, Tag}, Reply, Debug, StateName) ->
terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
case catch Mod:terminate(Reason, StateName, StateData) of
{'EXIT', R} ->
error_info(R, Name, Msg, StateName, StateData, Debug),
error_info(Mod, R, Name, Msg, StateName, StateData, Debug),
exit(R);
_ ->
case Reason of
@ -639,12 +640,12 @@ terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
[self(), Limit]),
exit(shutdown);
_ ->
error_info(Reason, Name, Msg, StateName, StateData, Debug),
error_info(Mod, Reason, Name, Msg, StateName, StateData, Debug),
exit(Reason)
end
end.
error_info(Reason, Name, Msg, StateName, StateData, Debug) ->
error_info(Mod, Reason, Name, Msg, StateName, StateData, Debug) ->
Reason1 =
case Reason of
{undef,[{M,F,A}|MFAs]} ->
@ -662,12 +663,16 @@ error_info(Reason, Name, Msg, StateName, StateData, Debug) ->
_ ->
Reason
end,
StateToPrint = case erlang:function_exported(Mod, print_state, 1) of
true -> (catch Mod:print_state(StateData));
false -> StateData
end,
Str = "** State machine ~p terminating \n" ++
get_msg_str(Msg) ++
"** When State == ~p~n"
"** Data == ~p~n"
"** Reason for termination = ~n** ~p~n",
format(Str, [Name, get_msg(Msg), StateName, StateData, Reason1]),
format(Str, [Name, get_msg(Msg), StateName, StateToPrint, Reason1]),
sys:print_log(Debug),
ok.