Erlang - 主管和 gen_fsm 上的异常退出
Posted
技术标签:
【中文标题】Erlang - 主管和 gen_fsm 上的异常退出【英文标题】:Erlang - Exception exit on supervisor and gen_fsm 【发布时间】:2015-05-24 05:47:53 【问题描述】:我有 3 个模块:calculadora
、log_calculadora
和 supervisor_calculadora
。 Calculadora
只是一个简单的计算器,它使用 gen_fsm 进行求和、减法、乘法和除法,supervisor 实现了supervisor 行为。 Calculadora 运行良好,但是当我尝试使用主管时,当您进行除法 0/0 或异常时必须重新启动 calculadora
模块时,它不起作用。为什么?
PD:模块log_calculadora
只是将我在calculadora
中所做的操作写入log.txt 文件中。 TEST 模块是给我异常退出的模块。
计算器:
-module(calculadora).
-author("BreixoCF").
-behaviour(gen_fsm).
%% Public API
-export([on/0, off/0, modo/1, acumular/1]).
%% Internal API (gen_fsm)
-export([init/1, handle_event/3, terminate/3]).
%% Internal API (estados)
-export([suma/2, suma/3, resta/2, resta/3, producto/2, producto/3, division/2, division/3]).
-define(CALC, calculadora).
-define(LOG_MODULE, log_calculadora).
-define(LOG_FILE, "log.txt").
%%%===================================================================
%%% Public API
%%%===================================================================
-spec on() -> ok.
on() ->
gen_fsm:start_link(local, ?CALC, ?CALC, 0, []).
-spec off() -> ok.
off() ->
gen_fsm:send_all_state_event(?CALC, stop).
-spec modo(O::atom())-> ok.
modo(suma) ->
gen_fsm:send_event(?CALC, modoSuma);
modo(resta) ->
gen_fsm:send_event(?CALC, modoResta);
modo(producto) ->
gen_fsm:send_event(?CALC, modoProducto);
modo(division) ->
gen_fsm:send_event(?CALC, modoDivision).
-spec acumular(N::number()) -> number().
acumular(N) ->
gen_fsm:sync_send_event(?CALC, numero, N).
%%%===================================================================
%%% Internal API (gen_fsm)
%%%===================================================================
init(Ac) ->
ok, _Pid = gen_event:start_link(local, logcalc),
gen_event:add_handler(logcalc, ?LOG_MODULE, ?LOG_FILE),
gen_event:notify(logcalc, on, []),
ok, suma, Ac.
handle_event(stop, _Estado, _DatosEstado) ->
stop, normal, [].
terminate(normal, _Estado, _DatosEstado) ->
gen_event:notify(logcalc, off),
gen_event:stop(logcalc),
ok.
%%%===================================================================
%%% Internal API (estados)
%%%===================================================================
% Cambio Modo SUMA
suma(modoSuma, Ac) ->
gen_event:notify(logcalc, cambio, suma, suma),
next_state, suma, Ac;
suma(modoResta, Ac) ->
gen_event:notify(logcalc, cambio, suma, resta),
next_state, resta, Ac;
suma(modoProducto, Ac) ->
gen_event:notify(logcalc, cambio, suma, producto),
next_state, producto, Ac;
suma(modoDivision, Ac) ->
gen_event:notify(logcalc, cambio, suma, division),
next_state, division, Ac.
% Cálculo Modo SUMA
suma(numero, N, _From, Ac) ->
gen_event:notify(logcalc, operacion, suma, N, Ac, Ac+N),
reply, Ac+N, suma, Ac+N.
% Cambio Modo RESTA
resta(modoSuma, Ac) ->
gen_event:notify(logcalc, cambio, resta, suma),
next_state, suma, Ac;
resta(modoResta, Ac) ->
gen_event:notify(logcalc, cambio, resta, resta),
next_state, resta, Ac;
resta(modoProducto, Ac) ->
gen_event:notify(logcalc, cambio, resta, producto),
next_state, producto, Ac;
resta(modoDivision, Ac) ->
gen_event:notify(logcalc, cambio, resta, division),
next_state, division, Ac.
% Cálculo Modo RESTA
resta(numero, N, _From, Ac) ->
gen_event:notify(logcalc, operacion, resta, N, Ac, Ac-N),
reply, Ac-N, resta, Ac-N.
% Cambio Modo PRODUCTO
producto(modoSuma, Ac) ->
gen_event:notify(logcalc, cambio, producto, suma),
next_state, suma, Ac;
producto(modoResta, Ac) ->
gen_event:notify(logcalc, cambio, producto, resta),
next_state, resta, Ac;
producto(modoProducto, Ac) ->
gen_event:notify(logcalc, cambio, producto, producto),
next_state, producto, Ac;
producto(modoDivision, Ac) ->
gen_event:notify(logcalc, cambio, producto, division),
next_state, division, Ac.
% Cálculo Modo PRODUCTO
producto(numero, N, _From, Ac) ->
gen_event:notify(logcalc, operacion, producto, N, Ac, Ac*N),
reply, Ac*N, producto, Ac*N.
% Cambio Modo DIVISION
division(modoSuma, Ac) ->
gen_event:notify(logcalc, cambio, division, suma),
next_state, suma, Ac;
division(modoResta, Ac) ->
gen_event:notify(logcalc, cambio, division, resta),
next_state, resta, Ac;
division(modoProducto, Ac) ->
gen_event:notify(logcalc, cambio, division, producto),
next_state, producto, Ac;
division(modoDivision, Ac) ->
gen_event:notify(logcalc, cambio, division, division),
next_state, division, Ac.
% Cálculo Modo DIVISION
division(numero, N, _From, Ac) ->
gen_event:notify(logcalc, operacion, division, N, Ac, Ac/N),
reply, Ac/N, division, Ac/N.
主管:
-module(supervisor_calculadora).
-author("BreixoCF").
-behaviour(supervisor).
%% API
-export([start/0]).
%% Supervisor callbacks
-export([init/1]).
%%%===================================================================
%%% API functions
%%%===================================================================
start() ->
supervisor:start_link(local, ?MODULE, ?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
init([]) ->
RestartStrategy = one_for_one,
MaxRestarts = 10,
MaxSecondsBetweenRestarts = 5,
Flags = RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts,
Restart = permanent,
Shutdown = 2000,
Type = worker,
Calculadora = calculadora, calculadora, on, [],
Restart, Shutdown, Type, [calculadora],
ok, Flags, [Calculadora].
测试模块
-module(calculadora_supervisada_statem).
-behaviour(proper_statem).
-include_lib("proper/include/proper.hrl").
%% CALLBACKS from proper_statem
-export([initial_state/0, command/1, precondition/2, postcondition/3, next_state/3]).
-export([suma/2, resta/2, producto/2, division/2]).
-export([acumular/1, modo/1]).
initial_state() ->
suma, 0.
command(_S) ->
frequency([25, call, ?MODULE, acumular, [number()],
20, call, ?MODULE, modo, [modo()]]).
modo() ->
elements([suma, resta, producto, division, unknown]).
next_state(division, _S, _V, call, ?MODULE, acumular, [0]) ->
suma, 0;
next_state(Op, S, _V, call, ?MODULE, acumular, [N]) ->
Op, erlang:apply(?MODULE, Op, [S, N]);
next_state(_Op, _S, _V, call, ?MODULE, modo, [unknown]) ->
suma, 0;
next_state(_Op, S, _V, call, ?MODULE, modo, [NewOp]) ->
NewOp, S;
next_state(S, _V, call, _, _, _) ->
S.
precondition(_S, call, _, _, _) ->
true.
postcondition(division, _S, call, ?MODULE, acumular, [0], 'EXIT',_) ->
true;
postcondition(Op, S, call, ?MODULE, acumular, [N], Res) ->
Res == ?MODULE:Op(S, N);
postcondition(_Op, _S, call, ?MODULE, modo, [_NewOp], _Res) ->
true;
postcondition(_S, call, _, _, _, _Res) ->
false.
prop_calculadora() ->
?FORALL(Cmds, commands(?MODULE),
begin
supervisor_calculadora:start(),
H, S, Res = run_commands(?MODULE,Cmds),
cleanup(),
?WHENFAIL(io:format("History: ~p\nState: ~p\nRes: ~p\n", [H, S, Res]),
aggregate(command_names(Cmds), Res == ok))
end).
%%--------------------------------------------------------------------
%% Internal wrappers and auxiliary functions
%%--------------------------------------------------------------------
acumular(N) ->
Acc = (catch calculadora:acumular(N)),
timer:sleep(100),
Acc.
modo(O) ->
Acc = (catch calculadora:modo(O)),
timer:sleep(100),
Acc.
cleanup() ->
catch calculadora:off(),
timer:sleep(100).
suma(A, B) ->
A + B.
resta(A, B) ->
A - B.
producto(A, B) ->
A * B.
division(_A, 0) ->
0;
division(A, B) ->
A / B.
【问题讨论】:
你得到什么错误? 正确:快速检查(calculadora_supervisada_statem:prop_calculadora())。 ..........** 异常退出:关机 如果您的主管在测试模块之外工作,您是否尝试过。我没有对其进行测试,但子规范看起来是正确的,并且 fsm 应该重新启动。该错误表明模块 calculadora_supervisada_statem 崩溃,这并不意味着主管 + fsm 没有完成他们的工作。如果没有关于proper_statem 行为、proper.hrl 和quickcheck 函数的信息,就很难走得更远。 【参考方案1】:proper:quickcheck(calculadora_supervisada_statem:prop_calculadora()).
触发以下
prop_calculadora() ->
?FORALL(Cmds, commands(?MODULE),
begin
supervisor_calculadora:start(),
在上面的代码中,我看到为每个测试用例启动了 supervisor_calculadora(quickcheckl ?FORALL 为每个 Cmd 执行块)。它将尝试启动主管并使用相同的名称再次注册它并失败。
【讨论】:
我修好了。谢谢你们的cmets。以上是关于Erlang - 主管和 gen_fsm 上的异常退出的主要内容,如果未能解决你的问题,请参考以下文章
Erlang 中 gen_fsm:start/3 的唯一原子
将 Learn You Some Erlang 教程从 gen_fsm 转换为 gen_statem