2017-09-05 49 views
1

我有一個表模塊。當它作爲gen_server啓動時,它會從Clock模塊創建2個服務器 - 一個用於X播放器,另一個用於O播放器。Erlang服務器在定時器模塊超時後崩潰

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) ([email protected])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}]}]} 

被稱爲與timeout的gen_server是一個表服務器。當時鍾超時時,時鐘保持對錶的引用以向它們發送timeout消息,所以表可以取消其他邏輯並執行其他邏輯。

這裏是我的全部clock.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], []). 

和全table.erl

-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}. 

我感到困惑的是它似乎是從調用表(主機變量)崩潰超時但我沒有看到有關表代碼的任何堆棧跟蹤。

s:s(Pid, Msg)是方便gen_server:call(Pid, Msg)

如何調試導致崩潰的原因?

編輯:因爲timeout

改變了timeout原子clockdone具有二郎特殊情況。

現在得到這個崩潰:

=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}]}]} 

這要是我的理解對不對,意味着服務器< 0.566.0>死亡。

回答

2

您應該從OTP Design Principles User's Guide開始6.1 Simple Debugging

如果它沒有幫助嘗試使用dbg模塊從Runtime_Tools。看看gen_server:call/2。看起來異常是在https://github.com/erlang/otp/blob/master/lib/stdlib/src/gen_server.erl#L206處產生的,這表明catch gen:call(Host, '$gen_call', timeout)由於某種原因返回{'EXIT',{timeout,{gen_server,call,[<0.532.0>,timeout]}}}。你可以更深入地瞭解它爲什麼會發生。我隱約記得有一些技巧或捕獲,可能會導致gen:call/3可能會返回{'EXIT',Reason}而不是拋出一個例外,但我不記得細節,不知道這是你的麻煩的原因。如果你喜歡GUI,你也可以試試observer

無論如何創建Minimal, Complete, and Verifiable example將有助於很多獲得更好的答案你的問題。

+0

謝謝。我更改了'timeout'原子''我自己的'clockdone'的原子,並且出現超時異常,所以看起來我的服務器因爲其他原因而死亡。 – quantumpotato