Timer 计时器模块超时后Erlang服务器崩溃

Timer 计时器模块超时后Erlang服务器崩溃,timer,erlang,gen-server,Timer,Erlang,Gen Server,我有一个桌子模块。当它作为gen_服务器启动时,它会从时钟模块创建两个服务器—一个用于X播放器,另一个用于O播放器 10秒后,时钟将超时,此代码称为: updateTick(Delta, {{Time, ticking, _}, Host}) -> Now = Time - Delta, case Now of Now when Now > 0 -> {{Now, ticking, intervalRef()}, Host}; _ ->

我有一个桌子模块。当它作为gen_服务器启动时,它会从时钟模块创建两个服务器—一个用于X播放器,另一个用于O播放器

10秒后,时钟将超时,此代码称为:

updateTick(Delta, {{Time, ticking, _}, Host}) ->
  Now = Time - Delta,
  case Now of
    Now when Now > 0 ->
      {{Now, ticking, intervalRef()}, Host};
    _ ->
      gen_server:call(Host, timeout),
      {{0, out, none}, Host}
  end;
我预计后一条款将被废除

下面是我得到的碰撞:

Eshell V8.2  (abort with ^G) (tunnel@127.0.0.1)1> Clock.{{1000,paused,none},<0.532.0>} Clock.{{20.823899999999803,ticking,#Ref<0.0.1.603>},<0.532.0>}

=ERROR REPORT==== 4-Sep-2017::20:10:19 ===
** Generic server <0.536.0> terminating
** Last message in was {tick,25.099}
** When Server state == {{20.823899999999803,ticking,#Ref<0.0.1.603>},
                         <0.532.0>}
** Reason for termination ==
** {{timeout,{gen_server,call,[<0.532.0>,timeout]}},
    [{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
     {clock,updateTick,2,[{file,"src/clock.erl"},{line,27}]},
     {clock,handle_call,3,[{file,"src/clock.erl"},{line,35}]},
     {gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,615}]},
     {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,647}]},
     {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
和完整的
表格.erl

-module(clock).
-compile(export_all).

-define(STARTING, 1000).
-define(INTERVAL, 250).

init([Host]) -> 
  {ok, defaultState(Host)}.

defaultState(Host) ->
  {{?STARTING, paused, none}, Host}.

tickPid(Pid, Then) ->
  Delta = timer:now_diff(erlang:timestamp(), Then) / 10000,
  s:s(Pid, {tick, Delta}).

intervalRef() ->
 {ok, {_, Tref}} = timer:apply_after(?INTERVAL, ?MODULE, tickPid, [self(), erlang:timestamp()]),
 Tref.

updateTick(Delta, {{Time, ticking, _}, Host}) ->
  Now = Time - Delta,
  case Now of
    Now when Now > 0 ->
      {{Now, ticking, intervalRef()}, Host};
    _ ->
      s:s(Host, timeout),
      {{0, out, none}, Host}
  end;

updateTick(_, State) ->
  State.

handle_call({tick, Delta}, _, State) ->
  State2 = updateTick(Delta, State),
  {reply, State2, State2};
handle_call(info, _, State) ->
  {reply, State, State};
handle_call(pause, _, {{Time, ticking, Tref}, Host}) ->
  timer:cancel(Tref),
  State2 = {{Time, paused, none}, Host},
  {reply, State2, State2};
handle_call(start, _, {{Time, paused, _}, Host}) ->
  {ok, Tref} = timer:apply_after(?INTERVAL, ?MODULE, tickPid, [self(), erlang:timestamp()]),
  State2 = {{Time, ticking, Tref}, Host},
  {reply, State2, State2};
handle_call(stop, _From, State) ->
  {stop, normal, shutdown_ok, State};
handle_call(_, _, State) ->
  {reply, State, State}.

terminate(_, State = {{_,_, none}, _}) ->
    io:format("Clock.~p~n", [State]),
    ok;
terminate(_, State = {{_,_,Tref}, _}) ->
    timer:cancel(Tref),
    io:format("Clock.~p~n", [State]),
    ok.
code_change(_OldVsn, State, _Extra) ->
    {ok, State}. 
handle_cast(_, State) ->
    {noreply, State}.
handle_info(Msg, State) ->
  io:format("Unexpected message: ~p~n",[Msg]),
  {noreply, State }.

go(Host) ->
  gen_server:start_link(?MODULE, [Host], []).
-module(table).
-compile(export_all).
-behavior(gen_server).

-define(POSTGAME_TIMEOUT, 6000).

otherPlayer(x) -> o;
otherPlayer(o) -> x.

processRecentTaken(true) -> 1;
processRecentTaken(false) -> 0.

processResult({error, Error, _Board}, State) ->
  {{error, Error}, State};
processResult({playing, NewBoard, Slices, RecentTaken},   {Board, Status, Players}) ->
  % update recent taken
  CurrentPlayer = maps:get(current_player, Status),
  OtherPlayer = otherPlayer(CurrentPlayer),
  {OClock, OActions} = maps:get(OtherPlayer, Players),
  s:s(OActions, {recent_bonus, processRecentTaken(RecentTaken)}),
  % update slice bonus
  {CClock, CActions} = maps:get(CurrentPlayer, Players),
  Made = s:s(CActions, {made, Slices}),
  Status2 = case Made of
    {over, _} ->
      s:s(Board, {cycle, OtherPlayer}),
      s:s(CClock, pause),
      s:s(OClock, start),
      maps:put(current_player, OtherPlayer, Status);
    _ -> Status
  end,
  {{ok, NewBoard}, {Board, Status2, Players}};
processResult({Win, NewBoard, _Slices, _RecentTaken}, State) ->
  {{ok, NewBoard}, winGame(Win, State)}.

markAsFinished(Pid, _Timestamp) ->
  s:s(Pid, finished).

winGame(x, State) ->
  winGame(xWin, State);
winGame(o, State) ->
  winGame(oWin, State);
winGame(Win, {Board, Status, Players}) ->
  CurrentPlayer = maps:get(current_player, Status),
  OtherPlayer = otherPlayer(CurrentPlayer),
  {OClock, _} = maps:get(OtherPlayer, Players),
  CurrentPlayer = maps:get(current_player, Status),
  {CClock, _} = maps:get(CurrentPlayer, Players),
  s:s(OClock, pause),
  s:s(CClock, pause),
  Status2 = maps:put(result, Win, Status),
  {ok, _Tref} = timer:apply_after(?POSTGAME_TIMEOUT, ?MODULE, markAsFinished, [self(), erlang:timestamp()]),
  {Board, Status2, Players}.

handle_call({place, Action, Player, Position}, _, State = {Board, Status, _Players}) ->
% TODO: check for is playing
  CurrentPlayer = maps:get(current_player, Status),
  Result = s:s(Board, {place, CurrentPlayer, {Action, Player, Position}}),
  {Response, State2} = processResult(Result, State),
  {reply, Response, State2};
handle_call(timeout, TimeoutPid, State = {_Board, _Status, Players}) ->
  TimeoutPlayer = getPlayerForClockPid(TimeoutPid, Players),
  WinningPlayer = otherPlayer(TimeoutPlayer),
  {Res, State2} = winGame(WinningPlayer, State),
  {reply, Res, State2};
handle_call(finished, _, {Board, Status, Players}) ->
  Status2 = maps:put(result, finished, Status),
  State2 = {Board, Status2, Players},
  {reply, State2, State2};
handle_call(assign_players, _, {Board, Status}) ->
  Players = createPlayers(self()),
  State2 = {Board, Status, Players},
  {reply, State2, State2};
handle_call(info, _, State = {Board, Status, #{x := X, o := O}}) ->
  BoardInfo = s:s(Board, info),
  RX = playerInfo(X),
  RO = playerInfo(O),
  Res = #{board => BoardInfo,
          status => Status, 
          players => #{x => RX, o => RO}},
  {reply, Res, State};
handle_call(_, _, State) ->
  {reply, State, State}.

playerInfo({Clock, Actions}) ->
  {Next, Current} = s:s(Actions, info),
  {{Time, _ ,_}, _} = s:s(Clock, info),
  #{clock => Time, actions => #{next => Next, current => Current}}.

getPlayerForClockPid(ClockPid, Players) ->
  getPlayerForClockPid(ClockPid, Players, maps:keys(Players)).
getPlayerForClockPid(ClockPid, Players, [H | T]) ->
  case maps:get(H, Players) of
    {ClockPid, _} -> H,
    getPlayerForClockPid(ClockPid, Players, T)
  end.

actionProcess(x) -> actions:go(1);
actionProcess(o) -> actions:go(2).

playerProcesses(Pid, Player) ->
  {ok, Clock} = clock:go(Pid),
  {ok, Actions} = actionProcess(Player),
  {Clock, Actions}.

playerNames() ->
  [x, o].

createPlayers(Self) ->
  createPlayers(Self, playerNames(), #{}).
createPlayers(_Self, [], Players) ->
  Players;
createPlayers(Self, [H | T], Players) ->
  createPlayers(Self, T, maps:put(H, playerProcesses(Self, H), Players)).

defaultStatus() ->
  #{current_player => x,
    result => playing}.

init([]) -> 
  {ok, Board} = board:go(),
  Status = defaultStatus(),
  {ok, {Board, Status}}.

go() ->
  {ok, Pid} = gen_server:start_link(?MODULE, [], []),
  s:s(Pid, assign_players),
  {ok, Pid}.

terminate(_, State = {_Board, Status, Players}) ->
  % gen_server:stop(Board),
  CurrentPlayer = maps:get(current_player, Status),
  OtherPlayer = otherPlayer(CurrentPlayer),
  {OClock, OActions} = maps:get(OtherPlayer, Players),
  CurrentPlayer = maps:get(current_player, Status),
  {CClock, CActions} = maps:get(CurrentPlayer, Players),
  gen_server:stop(OClock),
  gen_server:stop(CClock),
  gen_server:stop(OActions),
  gen_server:stop(CActions),
  io:format("Table Terminating.~p~n", [State]),
  ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}. 

handle_cast(_, State) ->
    {noreply, State}.

handle_info(Msg, State) ->
  io:format("Unexpected message: ~p~n",[Msg]),
  {noreply, State}.
我感到困惑的是,它似乎因为调用带有超时的表(主机变量)而崩溃,但我没有看到任何关于表代码的stacktrace

s:s(Pid,Msg)
gen\u server:call(Pid,Msg)
提供了便利

我如何调试导致崩溃的原因

编辑:

timeout
atom更改为
clockdone
,因为
timeout
在Erlang中有特殊情况

现在我们来看看这个崩溃:

=ERROR REPORT==== 5-Sep-2017::11:37:07 ===
** Generic server <0.570.0> terminating
** Last message in was {tick,25.1116}
** When Server state == {{20.927900000000147,ticking,#Ref<0.0.5.642>},
                         <0.566.0>}
** Reason for termination ==
** {{timeout,{gen_server,call,[<0.566.0>,clockdone]}},
    [{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
     {clock,updateTick,2,[{file,"src/clock.erl"},{line,27}]},
     {clock,handle_call,3,[{file,"src/clock.erl"},{line,35}]},
     {gen_server,try_handle_call,4,[{file,"gen_server.erl"},{line,615}]},
     {gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,647}]},
     {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,247}]}]}
=错误报告===2017年9月5日::11:37:07===
**通用服务器终止
**最后一条消息是{tick,25.1116}
**当服务器状态=={{20.927900000000147,滴答声,#Ref},
}
**终止的理由==
**{{timeout,{gen_server,call,[,clockdone]},
[{gen_server,call,2,[{file,“gen_server.erl”},{line,204}]},
{clock,updateTick,2,[{file,“src/clock.erl”},{line,27}]},
{clock,handle_uCall,3,[{file,“src/clock.erl”},{line,35}]},
{gen_server,try_handle_call,4,[{file,“gen_server.erl”},{line,615}]},
{gen_server,handle_msg,5,[{file,“gen_server.erl”},{line,647}]},
{proc_lib,init_p_do_apply,3,[{file,“proc_lib.erl”},{line,247}]}
如果我理解正确,这意味着服务器已死亡。

您应该从开始

如果没有帮助,请尝试使用中的模块。查看
genu服务器:call/2
。似乎生成了异常,该异常指示由于某种原因,
catch gen:call(Host,$gen_call',timeout)
返回
{EXIT',{timeout,{gen_server,call,[,timeout]}}
。你可以深入一点,看看为什么会这样。我模模糊糊地记得有一个诡计或陷阱,它可能会导致
gen:call/3
返回
{'EXIT',Reason}
,而不是抛出异常,但我记不起细节,也不知道这是您遇到麻烦的原因

如果您确实喜欢GUI,也可以尝试一下


无论如何,创建将有助于更好地回答您的问题。

谢谢。我更改了
timeout
atom`我自己的
clockdone
的atom,并且我得到了一个超时异常,因此我的服务器似乎因为其他原因而死亡。