【发布时间】:2016-03-24 19:26:22
【问题描述】:
假设我有一个 gen_server 回调模块,g,sn-p 的代码如下所示:
start_link(Args) ->
gen_server:start_link(?MODULE, [Args], []).
process_packet(Ref, Packet) ->
gen_server:call(Ref, MsgPacket={process_packet, Packet}).
init(Args) ->
gen_server:cast(self(), MsgInit={init, Args}), %% delayed initialization
{ok, state_not_initialized}.
handle_call({process_packet, Packet}, #g_state{}=S) ->
{reply, Packet, S}.
handle_cast({init, Args}, _) ->
State = #g_state{} = do_init(Args),
{noreply, State}.
还有另一个 gen_server,t,它的工作是监听一个套接字,
如果收到一个特定的数据包,启动一个g 来处理这个数据包,
所以,t 中的一些代码看起来像这样:
handle_info({tcp, _Socket, Packet}, #t_state{}) ->
case g:start_link(WhatEver) of
{ok, Pid} ->
g:process_packet(Pid, Packet);
_ ->
not_interested
end.
让g 的 pid 为 PidG,t 的 pid 为 PidT。
我的问题是,MsgPacket(由PidT 发送给PidG)是否有可能在MsgInit(由PidG 发送给它自己)之前到达PidG?如果发生这种情况,PidG 将崩溃,因为state_not_initialized 与g 的handle_call 中的#g_state{} 不匹配。
我的猜测是这完全有可能,但我没有想出一种方法来产生这种情况。理想情况下,您可以减慢消息MsgInit 的消息传输速度,但我怀疑 Erlang 是否允许我做这种事情。知道如何让MsgPacket 在MsgInit 之前到达吗?
修复相对容易,(假设我的猜测是正确的),您只需在g 启动之后,PidG 的do_init 在PidT 中发送receive 一些ack,之前进行 gen 调用。
更新
假设我的猜测是正确的,为了使问题更具体,如何使kickoff_many/1 启动的进程之一崩溃? (根据zxq9的例子修改)
-module(spawn_spammer).
-export([kickoff_many/1]).
kickoff() ->
{ok, Catcher} = spawn_catcher:start(),
{echo, _} = spawn_catcher:process_packet(Catcher, {packet_from, self()}).
kickoff_many(N) ->
lists:foreach(fun(_) -> spawn(fun kickoff/0) end, lists:seq(1, N)).
-module(spawn_catcher).
-behavior(gen_server).
-export([start/0,
process_packet/2,
init/1,
handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start() ->
gen_server:start(?MODULE, [], []).
process_packet(Ref, Packet) ->
gen_server:call(Ref, {process_packet, Packet}).
init(_) ->
gen_server:cast(self(), get_ready),
{ok, not_ready}.
handle_cast(get_ready, not_ready) ->
{noreply, ready}.
handle_call({process_packet, P}, _From, ready) ->
{stop, normal, {echo, P}, ready};
handle_call({process_packet, _P}, _From, not_ready) ->
{stop, normal, call_while_not_ready, not_ready}.
handle_info(_, ready) ->
{stop, normal, unexpected, ready}.
terminate(_, _) -> ok.
code_change(_, State, _) -> {ok, State}.
【问题讨论】:
标签: erlang erlang-otp