简体   繁体   English

使用start_link启动时,GenServer不会trap_exit

[英]GenServer doesn't trap_exit when started with start_link

I found that when trying to trap exit signal, starting a GenServer with GenServer.start then Process.link the pid has very different outcome than running GenServer.start_link . 我发现当尝试捕获退出信号时,使用GenServer.start启动GenServer.start然后进行Process.link ,pid与运行GenServer.start_link结果GenServer.start_link

Here's the experiment code I used to demonstrate the problem: 这是我用来演示问题的实验代码:

defmodule Foo do
  defmodule Server do
    def init(_) do
      Process.flag(:trap_exit, true)
      {:ok, nil}
    end
    def handle_info({:EXIT, from, reason}, _) do
      IO.inspect({:kill_signal, from, reason})
      {:noreply, nil}
    end
  end

  def foo() do
    Process.flag(:trap_exit, true)
    # version 1
    {:ok, pid} = GenServer.start_link(Server, nil)

    # version 2
    # {:ok, pid} = GenServer.start(Server, nil)
    # Process.link(pid)

    # print process info
    IO.inspect({self(), pid, Process.info(pid)})

    Process.exit(pid, :reason)

    :timer.sleep(200)
  end
end

Foo.foo

With version 1, the EXIT signal cause the Server to exit without being caught in its handle_info block, but with version 2, the signal was correctly intercepted and processed in handle_info block, and therefore Server is not terminated. 对于版本1,EXIT信号导致Server退出而不会被其handle_info块捕获,但是对于版本2,信号在handle_info块中被正确拦截和处理,因此Server不会终止。 I also notice that the Process.info of these two ways to start a GenServer are identical. 我还注意到,启动GenServer的这两种方式的Process.info是相同的。

I tried the same thing with spawn_link and spawn , but they all behaves as expected - EXIT signal all being caught - there's no difference: 我用spawn_linkspawn尝试了同样的事情,但它们都表现得像预期的那样 - EXIT信号都被捕获 - 没有区别:

defmodule Foo do
  def loop do
    Process.flag(:trap_exit, true)
    receive do
      msg -> IO.inspect(msg)
    end
    loop
  end

  def foo() do
    Process.flag(:trap_exit, true)
    # version 1
    pid = spawn_link(&loop/0)

    # version 2
    # pid = spawn(&loop/0)
    # Process.link(pid)

    # print process info
    IO.inspect({self(), pid, Process.info(pid)})

    Process.exit(pid, :reason)

    :timer.sleep(200)
  end
end

Foo.foo

By the way I'm using Elixir 1.8.1 on Erlang/OTP 21, if it matters. 顺便说一句,我在Erlang / OTP 21上使用Elixir 1.8.1,如果重要的话。

I want to know what causes the difference in behaviors, is this a bug or by design, and how I can trap EXIT correctly if I want to call start+link atomically. 我想知道导致行为差异的原因,这是一个错误还是设计,以及如果我想以原子方式调用start + link,我如何正确捕获EXIT。

handle_info is not being called because the one sending the exit signal is the parent process. handle_info未被调用,因为发送退出信号的是父进程。 GenServer and all other behaviours handles the parent exit signal and always shuts down when the parent does, mainly because if you are in a supervision tree and your supervisor shuts down, you want to terminate immediately too as at that moment all bets are off. GenServer和所有其他行为处理父出口信号并在父进程时始终关闭,主要是因为如果您在监督树中并且您的主管关闭,您希望立即终止,因为此时所有投注都已关闭。 If you replace this: 如果你替换这个:

Process.exit(pid, :reason)

By: 通过:

spawn fn -> Process.exit(pid, :reason) end

You can see handle_info called as another process is sending the exit signal. 您可以看到handle_info被调用,因为另一个进程正在发送退出信号。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM