From 83327c71dd35a9a4ddf1bc8e70b6a983dc282d40 Mon Sep 17 00:00:00 2001 From: Alain Zscheile Date: Sun, 27 Nov 2022 23:12:03 +0100 Subject: [PATCH] prevent dead-lock during garbage-collection --- lib/floof/packet_spool.ex | 85 +++++++++++++++--------------------- lib/floof/session_manager.ex | 22 ++++++---- 2 files changed, 49 insertions(+), 58 deletions(-) diff --git a/lib/floof/packet_spool.ex b/lib/floof/packet_spool.ex index 285e6f6..cba2678 100644 --- a/lib/floof/packet_spool.ex +++ b/lib/floof/packet_spool.ex @@ -46,10 +46,6 @@ defmodule Floof.PacketSpool do @impl true def init(state) do :ok = File.mkdir_p(state) - - # in 5 seconds - Process.send_after(self(), :collect_garbage, 5 * 1000) - {:ok, state} end @@ -134,59 +130,50 @@ defmodule Floof.PacketSpool do end @impl true - def handle_cast({:drop, hash}, state) do - case File.rm(hash_to_path(hash, state)) do - :ok -> nil - {:error, e} -> Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}") + def handle_call({:collect_garbage_keep_only, to_keep}, _, state) do + {:ok, items} = File.ls(state) + + present = + MapSet.new( + Enum.flat_map(items, fn x -> + case Base.url_decode64(x) do + {:ok, y} -> + [y] + + {:error, e} -> + Logger.warn("spool: unable to decode entry name #{x}: #{inspect(e)}") + [] + end + end) + ) + + to_keep = MapSet.new(to_keep) + to_remove = MapSet.difference(present, to_keep) + + if not Enum.empty?(to_remove) do + Logger.debug("spool: to_remove #{inspect(to_remove)}") + + for hash <- to_remove do + case File.rm(hash_to_path(hash, state)) do + :ok -> + nil + + {:error, e} -> + Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}") + end + end end {:noreply, state} end @impl true - def handle_info(:collect_garbage, state) do - case GenServer.call(Floof.SessionManager, :get_keep_only, :infinity) do - :error -> - nil - - {:ok, to_keep} -> - {:ok, items} = File.ls(state) - - present = - MapSet.new( - Enum.flat_map(items, fn x -> - case Base.url_decode64(x) do - {:ok, y} -> - [y] - - {:error, e} -> - Logger.warn("spool: unable to decode entry name #{x}: #{inspect(e)}") - [] - end - end) - ) - - to_keep = MapSet.new(to_keep) - to_remove = MapSet.difference(present, to_keep) - - if not Enum.empty?(to_remove) do - Logger.debug("spool: to_remove #{inspect(to_remove)}") - - for hash <- to_remove do - case File.rm(hash_to_path(hash, state)) do - :ok -> - nil - - {:error, e} -> - Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}") - end - end - end + def handle_cast({:drop, hash}, state) do + case File.rm(hash_to_path(hash, state)) do + :ok -> nil + {:error, e} -> Logger.error("spool: unable to remove #{inspect(hash)}: #{inspect(e)}") end - # do this again in 15 seconds - Process.send_after(self(), :collect_garbage, 15 * 1000) - {:noreply, state} end diff --git a/lib/floof/session_manager.ex b/lib/floof/session_manager.ex index faf9b32..ea1d269 100644 --- a/lib/floof/session_manager.ex +++ b/lib/floof/session_manager.ex @@ -15,6 +15,7 @@ defmodule Floof.SessionManager do @impl true def init({file, opts}) do {:ok, file} = :dets.open_file(file, opts ++ [type: :bag]) + Process.send_after(self(), :collect_garbage, 5 * 1000) {:ok, %Floof.SessionManager{dets_file: file}} end @@ -143,16 +144,19 @@ defmodule Floof.SessionManager do end @impl true - def handle_call(:get_keep_only, _, state) do - {:reply, - case :dets.match(state.dets_file, {'_', '$2'}) do - {:error, e} -> - Logger.error("garbage collection error: #{inspect(e)}") - :error + def handle_info(:collect_garbage, state) do + case :dets.match(state.dets_file, {'_', '$2'}) do + {:error, e} -> + Logger.error("garbage collection error: #{inspect(e)}") + Process.send_after(self(), :collect_garbage, 60 * 1000) - keys -> - {:ok, Enum.map(keys, fn [x] -> x end)} - end, state} + keys -> + to_keep = Enum.map(keys, fn [x] -> x end) + GenServer.call(Floof.PacketSpool, {:collect_garbage_keep_only, to_keep}, :infinity) + Process.send_after(self(), :collect_garbage, 15 * 1000) + end + + {:noreply, state} end defp all_session_keys(state) do