handle_info: DOWN не вызывается после смерти отслеживаемого процесса - PullRequest
0 голосов
/ 15 ноября 2018

У меня проблема с Process.monitor/1.Мой первоначальный вариант использования заключался в мониторинге канала Phoenix и выполнении некоторой очистки после его смерти.Однако мне не удалось настроить его в Фениксе, и я решил протестировать его на чистых GenServers.

Итак, у меня есть простой GenServer, и я хочу отследить, когда он умрет:

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

И еще один GenServer, который отслеживает:

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

Так что, если я правильно понимаю, Process.monitor начнет мониторинг процесса Temp.Server и должен вызвать handle_info match :DOWN когда Server процесс умирает.Если я попробую это в iex:

iex> {_, pid} = Temp.Server.start_link([])
{:ok, #PID<0.23068.3>}                    
iex> Process.exit(pid, :kill)             
true     

, я ожидаю, что handle_info будет вызван из модуля Monitor и войдет в систему "ВНИЗ", однако этого не произойдет.Что я делаю неправильно?Я предполагаю, что это не работает, потому что я вызываю монитор из процесса сервера Temp.Monitor.monitor(self()), но я просто не могу понять, как еще мне это сделать.

1 Ответ

0 голосов
/ 15 ноября 2018

Когда вы вызываете метод Temp.Monitor.monitor/1, он все еще выполняется в собственном процессе Temp.Server, а не Temp.Monitor. Это означает, что сообщение :DOWN отправляется на Temp.Server, когда Temp.Server умирает, что является избыточным.

Что вы хотите сделать, так это передать pid вашего серверного процесса в Temp.Monitor и заставить его вызывать метод Process.Monitor из своего собственного процесса, чтобы он мог его отслеживать. Это может произойти только из одного из обратных вызовов GenServer .

Вы можете сделать это, переместив свою реализацию в handle_call/3 или 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
...