Erlang - 主管和 gen_fsm 异常退出

Erlang - Exception exit on supervisor and gen_fsm

我有 3 个模块:calculadoralog_calculadorasupervisor_calculadoraCalculadora 只是一个简单的计算器,它使用 gen_fsm 进行求和、减法、乘法和除法,而监督者实现监督者的行为。 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.
proper:quickcheck(calculadora_supervisada_statem:prop_calculadora()).

触发以下

 prop_calculadora() ->
    ?FORALL(Cmds, commands(?MODULE),
        begin
        supervisor_calculadora:start(),

在上面的代码中,我看到每个测试用例都启动了 supervisor_calculadora(quickchecl ?FORALL 为每个 Cmd 执行块)。它将尝试启动主管并以相同的名称再次注册它,但失败了。