I am having an issue with Process.monitor/1
. My initial use case was to monitor Phoenix Channel and do some cleanup after it dies. However, I didn't manage to set it up in Phoenix and decided to test it out with pure GenServers.
So, I have a simple GenServer
and I want to track when it dies:
defmodule Temp.Server do
use GenServer
def start_link(_), do: GenServer.start_link(__MODULE__, %{})
def init(args) do
Temp.Monitor.monitor(self())
{:ok, args}
end
end
And another GenServer that monitors:
defmodule Temp.Monitor do
use GenServer
require Logger
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def monitor(pid) do
Process.monitor(pid)
end
def handle_info({:DOWN, ref, :process, _, _}, state) do
Logger.info("DOWN")
{:noreply, state}
end
end
So, if I understand correctly, Process.monitor
will start monitoring the Temp.Server
process, and should call the handle_info
matching :DOWN
when Server
process dies. If I try it in iex
:
iex> {_, pid} = Temp.Server.start_link([])
{:ok, #PID<0.23068.3>}
iex> Process.exit(pid, :kill)
true
I expect handle_info
being called from Monitor
module and logging "DOWN", however that doesn't happen. What am I doing wrong? I assume it doesn't work because I call to monitor from Server process Temp.Monitor.monitor(self())
, but I just can't figure out how else should I do it.
When you call the Temp.Monitor.monitor/1
method, it's still running in Temp.Server
's own process, not Temp.Monitor
's. That means the :DOWN
message is sent to Temp.Server
when Temp.Server
dies, which is redundant.
What you want to do is, pass the pid
of your server process to Temp.Monitor
and have it call the Process.Monitor
method from it's own process so it can monitor it. That can only happen from one of the GenServer callbacks.
You can do that by moving your implementation in to handle_call/3
or handle_cast/3
:
defmodule Temp.Monitor do
use GenServer
require Logger
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def monitor(pid) do
GenServer.cast(__MODULE__, {:monitor, pid})
end
def handle_cast({:monitor, pid}, state) do
Process.monitor(pid)
{:noreply, state}
end
def handle_info({:DOWN, ref, :process, _, _}, state) do
Logger.info("DOWN")
{:noreply, state}
end
end