1defmodule Phoenix.PubSub.GC do
2  @moduledoc """
3  A garbage collector process that cleans up the table used
4  by `Phoenix.PubSub.Local`.
5  """
6
7  use GenServer
8
9  @doc """
10  Starts the server.
11
12    * `server_name` - The name to register the server under
13    * `table_name` - The name of the local table
14
15  """
16  def start_link(server_name, local_name) do
17    GenServer.start_link(__MODULE__, {server_name, local_name}, name: server_name)
18  end
19
20  @doc """
21  Force table clean up because the given pid is down asynchronously.
22
23    * `gc_server` - The registered server name or pid
24    * `pid` - The subscriber pid
25
26  ## Examples
27
28      iex> down(:gc_server, self())
29      :ok
30
31  """
32  def down(gc_server, pid) when is_atom(gc_server) do
33    GenServer.cast(gc_server, {:down, pid})
34  end
35
36  def init({server_name, local_name}) do
37    {:ok, %{topics: local_name, pids: server_name}}
38  end
39
40  def handle_call({:subscription, pid}, _from, state) do
41    {:reply, subscription(state.pids, pid), state}
42  end
43
44  def handle_cast({:down, pid}, state) do
45    try do
46      topics = :ets.lookup_element(state.pids, pid, 2)
47      for topic <- topics do
48        true = :ets.match_delete(state.topics, {topic, {pid, :_}})
49      end
50      true = :ets.match_delete(state.pids, {pid, :_})
51    catch
52      :error, :badarg -> :badarg
53    end
54
55    {:noreply, state}
56  end
57
58  defp subscription(pids_table, pid) do
59    try do
60      :ets.lookup_element(pids_table, pid, 2)
61    catch
62      :error, :badarg -> []
63    end
64  end
65end
66