Why does GenServer run with start_link and start + link behave differently with exit signal?

Here is a simple test GenServer:

defmodule T do
  use GenServer

  def start() do
    IO.puts("T.start")
    GenServer.start(__MODULE__, [])
  end

  def start_link() do
    IO.puts("T.start_link")
    GenServer.start_link(__MODULE__, [])
  end

  @impl true
  def init(state) do
    IO.puts("T.init with trap_exit enable ")
    Process.flag(:trap_exit, true)
    {:ok, state}
  end

  @impl true
  def handle_info(msg, state) do
    IO.inspect(msg, label: "T.handle_info")
    {:noreply, state}
  end

  @impl true
  def terminate(reason, state) do
    IO.inspect(reason, label: "T.terminate")
  end
end

And here are two iex executions that disorientate my understanding of GenServer operation with exit signal:

Interactive Elixir (1.13.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> self()
#PID<0.126.0>
iex(2)> Process.flag(:trap_exit, true)
false
iex(3)> {:ok, p} = T.start_link()
T.start_link
T.init with trap_exit enable 
{:ok, #PID<0.130.0>}
iex(4)> Process.exit(p, :err)
true
T.terminate: :err
iex(5)> 
17:39:15.736 [error] GenServer #PID<0.130.0> terminating
** (stop) :err
Last message: {:EXIT, #PID<0.126.0>, :err}
State: []
 
nil
iex(6)> Process.alive?(p)
false
iex(7)> flush()
{:EXIT, #PID<0.130.0>, :err}
:ok
Interactive Elixir (1.13.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> self()
#PID<0.126.0>
iex(2)> Process.flag(:trap_exit, true)
false
iex(3)> {:ok, p} = T.start() 
T.start
T.init with trap_exit enable 
{:ok, #PID<0.130.0>}
iex(4)> Process.link(p)
true
iex(5)> Process.exit(p, :err)
T.handle_info: {:EXIT, #PID<0.126.0>, :err}
true
iex(6)> Process.alive?(p)
true
iex(7)> flush()
:ok

As you can see, GenServer started with start_link and with start and then link behave differently on the exit signal.
The second variant behaves as I suppose it must be. If process set :trap_exit, then exit signal (if it is not :kill) converted to a message.
But what is wrong with the first variant? Why does the exit signal not convert to a message though that process set :trap_exit?

I read all the doc about this topic that I could find, but unfortunately, I can’t find the answer. Could anyone please point me to corresponding documentation or at last explain this behavior.

It is funny, but as I posted this topic, with next google request I found the answer :slight_smile: - erlang - GenServer doesn't trap_exit when started with start_link - Stack Overflow

But for sure this knowledge about parent process is not something that you easily could find in elixir/erlang doc :man_shrugging:t2:.

1 Like