Supervisor.terminate_child doesn't call terminate callback on child

I was trying to stop children of a Supervisor through terminate_child/2 and expected that the GenServer callback terminate/2 is called on the child but it is not.
So now I am curious if this is correct and when to use Supervisor.terminate_child/2 over GenServer.stop/1.
Here is an example:

defmodule Test do
  use GenServer
  def start_link(_) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  def init(args) do
  {:ok, args}
  def handle_call(:test, _from, state) do
    {:reply, :test_reply, state}
  def terminate(_reason, _state) do

{:ok, pid} = Supervisor.start_link([Test], strategy: :one_for_one), :test)
|> IO.inspect()
# :test_reply

Supervisor.terminate_child(pid, Test)
|> IO.inspect()
# :ok 
# but no 'terminate'

Thanks for your insights!

You GenServer is not trapping exits so when the supervisor sends it an exit signal to terminate the GenServer it just immediately dies. You need to implement an init/1 callback in which you do, amongst other things, a Process.flag(:trap_exit, true) which then makes the GenServer process catch that exit signal, call the terminate/2 callback and then die.


Thanks! Sorry I should have read the docs more carefully. Its clearly written there:

1 Like