未调用 GenServer 超时处理程序

GenServer timeout handler not being called

我有一个非常简单的设置:一个 GenServer,一种缓存,它产生 child 个带有超时的 GenServer,它们通过 send 处理 parent关于他们不活动的消息。

child 通过测试确认它在指定的超时后发送 {:inactive, my_id}。问题是,只要 child 从未接收到获取其状态数据的调用,这种情况就会发生,在这种情况下,它永远不会超时。

为什么处理一个电话要防止超时?有没有办法在不阻塞超时的情况下处理调用?

这里是完整的测试用例:https://github.com/thure/so-genserver-timeout

Child:

defmodule GenServerTimeoutBattery.Child do
  use GenServer

  def start_link(child_id, timeout_duration, parent_pid) do
    GenServer.start_link(__MODULE__, [child_id, timeout_duration, parent_pid], [name: String.to_atom(child_id)])
  end

  def get_data(child_id) do
    GenServer.call(String.to_atom(child_id), :get_data)
  end

  @impl true
  def init([child_id, timeout_duration, parent_pid]) do
    IO.puts('Timeout of #{timeout_duration} set for')
    IO.inspect(child_id)
    {
      :ok,
      %{
        data: "potato",
        child_id: child_id,
        parent_process: parent_pid
      },
      timeout_duration
    }
  end

  @impl true
  def handle_call(:get_data, _from, state) do
    IO.puts('Get data for #{state.child_id}')
    {
      :reply,
      state.data,
      state
    }
  end

  @impl true
  def handle_info(:timeout, state) do
    # Hibernates and lets the parent decide what to do.
    IO.puts('Sending timeout for #{state.child_id}')
    if is_pid(state.parent_process), do: send(state.parent_process, {:inactive, state.child_id})

    {
      :noreply,
      state,
      :hibernate
    }
  end
end

测试:

defmodule GenServerTimeoutBattery.Tests do
  use ExUnit.Case

  alias GenServerTimeoutBattery.Child

  test "child sends inactivity signal on timeout" do
    id = UUID.uuid4(:hex)

    assert {:ok, cpid} = Child.start_link(id, 2000, self())

    # If this call to `get_data` is removed, test passes.
    assert "potato" == Child.get_data(id)

    assert_receive {:inactive, child_id}, 3000

    assert child_id == id

    assert :ok = GenServer.stop(cpid)
  end
end

原来在 init 上设置 timeout 会应用一个超时,该超时仅在收到呼叫或演员表之前有效。

每个调用或转换都可以设置自己的 timeout如果未指定 timeout,则默认为 :infinity 文档在这一点上没有明确说明,但现在对我来说很有意义。