We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Trapping exits so terminate/2 actually runs
almirsarajcic
A GenServer that flushes buffered work in terminate/2 loses it when its supervisor shuts it down, unless the process traps exits:
defmodule EventBuffer do
use GenServer
def start_link(opts), do: GenServer.start_link(__MODULE__, opts, name: __MODULE__)
@impl GenServer
def init(_opts) do
Process.flag(:trap_exit, true)
{:ok, []}
end
@impl GenServer
def terminate(reason, events) do
IO.puts("flushing #{length(events)} events (#{inspect(reason)})")
end
end
Stopping the supervisor with two events in the buffer:
Supervisor.stop(sup)
# without trap_exit: nothing
# with trap_exit: flushing 2 events (:shutdown)
The supervisor stops a child with a :shutdown exit signal. A process that isn’t trapping exits dies from that signal immediately, so terminate/2 never runs. With trap_exit, the signal becomes a message, and GenServer calls terminate/2 before exiting.
Trapping exits also turns a crash in any linked process into an {:EXIT, pid, reason} message instead of taking the server down. Without a matching handle_info/2, that only shows up as an “unexpected message” log. And terminate/2 still has to finish within the child’s :shutdown timeout, 5000 ms for workers by default, or the supervisor kills it anyway.
copied to clipboard