watcher.ex (1864B)
1 defmodule DBConnection.Watcher do 2 @moduledoc false 3 @name __MODULE__ 4 5 use GenServer 6 7 def start_link(_) do 8 GenServer.start_link(__MODULE__, :ok, name: @name) 9 end 10 11 def watch(supervisor, args) do 12 GenServer.call(@name, {:watch, supervisor, args}, :infinity) 13 end 14 15 def init(:ok) do 16 Process.flag(:trap_exit, true) 17 {:ok, {%{}, %{}}} 18 end 19 20 def handle_call({:watch, supervisor, args}, {caller_pid, _ref}, {caller_refs, started_refs}) do 21 case DynamicSupervisor.start_child(supervisor, args) do 22 {:ok, started_pid} -> 23 Process.link(caller_pid) 24 caller_ref = Process.monitor(caller_pid) 25 started_ref = Process.monitor(started_pid) 26 caller_refs = Map.put(caller_refs, caller_ref, {supervisor, started_pid, started_ref}) 27 started_refs = Map.put(started_refs, started_ref, {caller_pid, caller_ref}) 28 {:reply, {:ok, started_pid}, {caller_refs, started_refs}} 29 30 other -> 31 {:reply, other, {caller_refs, started_refs}} 32 end 33 end 34 35 def handle_info({:DOWN, ref, _, _, _}, {caller_refs, started_refs}) do 36 case caller_refs do 37 %{^ref => {supervisor, started_pid, started_ref}} -> 38 Process.demonitor(started_ref, [:flush]) 39 DynamicSupervisor.terminate_child(supervisor, started_pid) 40 {:noreply, {Map.delete(caller_refs, ref), Map.delete(started_refs, started_ref)}} 41 42 %{} -> 43 %{^ref => {caller_pid, caller_ref}} = started_refs 44 Process.demonitor(caller_ref, [:flush]) 45 Process.exit(caller_pid, :kill) 46 {:noreply, {Map.delete(caller_refs, caller_ref), Map.delete(started_refs, ref)}} 47 end 48 end 49 50 def handle_info({:EXIT, _, _}, state) do 51 {:noreply, state} 52 end 53 54 def terminate(_, {_, started_refs}) do 55 for {_, {caller_pid, _}} <- started_refs do 56 Process.exit(caller_pid, :kill) 57 end 58 59 :ok 60 end 61 end