我有一个表模块。当它作为gen_server启动时,它会从Clock模块创建2个服务器 - 一个用于X播放器,另一个用于O播放器。
10秒后,Clock将超时,并调用此代码:
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}]}]}
使用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}.
我感到困惑的是,它似乎是因为超时调用Table(Host变量)而崩溃,但我没有看到有关Table代码的堆栈跟踪。
s:s(Pid, Msg)
为gen_server:call(Pid, Msg)
提供了便利。
如何调试导致崩溃的原因?
编辑:
将timeout
原子更改为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}]}]}
如果我理解正确,则表示服务器&lt; 0.566.0&gt;死亡。
答案 0 :(得分:2)
您应该从6.1 Simple Debugging开始使用OTP Design Principles User's Guide。
如果无效,请尝试使用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}
而不是抛出异常,但我不记得细节,也不知道这是你麻烦的原因。< / p>
如果您喜欢GUI,也可以尝试observer。
无论如何,创建Minimal, Complete, and Verifiable example可以帮助您更好地回答您的问题。