Current section
Files
Jump to
Current section
Files
lib/brawlbrain.ex
defmodule Brawlex.BrawlBrain do
use GenServer
@moduledoc false
def start_link([]) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
# Callbacks
@impl true
def init(_x) do
# send message to initialize the dynamicsupervisor
send(self(), :init_dynsuper)
{:ok, {%{}, MapSet.new(), %{}}}
end
@impl true
def handle_info(:init_dynsuper, state) do
child_specs = {Brawlex.DynamicSupervisor, []}
{:ok, spid} = Supervisor.start_child(Brawlex.Supervisor, child_specs)
_sref = Process.monitor(spid)
{:noreply, {spid, state}}
end
@impl true
def handle_info({:DOWN, _ref, :process, spid, reason}, {spid, _state}) do
{:stop, {:supersuper_down, reason}, []}
end
@impl true
def handle_info({:DOWN, _ref, :process, tpid, _reason}, {spid, {tokens, token_dups, refs}}) do
case refs[tpid] do
nil ->
# not our problem
{:noreply, {spid, {tokens, refs}}}
token ->
new_tokens = Map.delete(tokens, token)
new_refs = Map.delete(refs, tpid)
{:noreply, {spid, {new_tokens, token_dups, new_refs}}}
end
end
@impl true
def handle_info(_msg, state) do
# not our problem
{:noreply, state}
end
# pattern match the MapSet
@impl true
def handle_call({:new_token, token_id}, _from, state = {spid, {tokens, token_dups, refs}}) do
case MapSet.member?(token_dups, token_id) do
true ->
case DynamicSupervisor.start_child(spid, {Brawlex.TokenProcess, token_id}) do
{:ok, child} ->
new_token_id = make_ref()
ref_child = Process.monitor(child)
new_tokens = Map.put(tokens, new_token_id, {ref_child, child})
new_refs = Map.put(refs, child, new_token_id)
{:reply, child, {spid, {new_tokens, token_dups, new_refs}}}
{:error, reason} ->
{:reply, {:error, {:unable_to_open, reason}}, state}
end
false ->
case tokens[token_id] do
nil ->
case DynamicSupervisor.start_child(spid, {Brawlex.TokenProcess, token_id}) do
{:ok, child} ->
ref_child = Process.monitor(child)
new_tokens = Map.put(tokens, token_id, {ref_child, child})
new_refs = Map.put(refs, child, token_id)
{:reply, child, {spid, {new_tokens, token_dups, new_refs}}}
{:error, reason} ->
{:reply, {:error, {:unable_to_open, reason}}, state}
end
{_ref, tpid} ->
{:reply, tpid, state}
end
end
end
@impl true
def handle_call({:new_token_allow_dup, token_id}, _from, state = {spid, {tokens, token_dups, refs}}) do
new_token_dups = MapSet.put(token_dups, token_id)
case DynamicSupervisor.start_child(spid, {Brawlex.TokenProcess, token_id}) do
{:ok, child} ->
new_token_id = make_ref()
ref_child = Process.monitor(child)
new_tokens = Map.put(tokens, new_token_id, {ref_child, child})
new_refs = Map.put(refs, child, new_token_id)
{:reply, child, {spid, {new_tokens, new_token_dups, new_refs}}}
{:error, reason} ->
{:reply, {:error, {:unable_to_open, reason}}, state}
end
end
@impl true
def handle_call(_msg, _from, state) do
{:noreply, state}
end
@impl true
def handle_cast({:close, token_id}, state = {spid, {tokens, token_dups, refs}}) do
case tokens[token_id] do
nil ->
{:noreply, state}
{ref, _tpid} ->
new_tokens = Map.delete(tokens, token_id)
new_refs = Map.delete(refs, ref)
Process.demonitor(ref, [:flush])
{:noreply, {spid, {new_tokens, token_dups, new_refs}}}
end
end
@impl true
def handle_cast(_msg, state) do
{:noreply, state}
end
### Interface ###
@spec open_connection(Brawlex.token(), boolean(), timeout()) :: {:ok, pid()} | {:error, any()}
def open_connection(token_id, allow_duplicates, timeout \\ 5000)
def open_connection(token_id, false, timeout) do
try do
GenServer.call(Brawlex.BrawlBrain, {:new_token, token_id}, timeout)
catch
:exit, error -> {:error, error}
else
tpid -> {:ok, tpid}
end
end
def open_connection(token_id, true, timeout) do
try do
GenServer.call(Brawlex.BrawlBrain, {:new_token_allow_dup, token_id}, timeout)
catch
:exit, error -> {:error, error}
else
tpid -> {:ok, tpid}
end
end
@spec close_connection(Brawlex.token()) :: :ok
def close_connection(token_id) do
GenServer.cast(Brawlex.BrawlBrain, {:close, token_id})
end
end