Skip to content

Commit

Permalink
Update to NimbleOwnership 1.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
wojtekmach committed Dec 6, 2024
1 parent 978b637 commit d3a6727
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 67 deletions.
100 changes: 45 additions & 55 deletions lib/req/test/ownership.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Vendored from nimble_ownership v1.0.0, replacing NimbleOwnership.Error with
# Vendored from nimble_ownership v1.0.1, replacing NimbleOwnership.Error with
# Req.Test.OwnershipError.
#
# Check changes with:
Expand Down Expand Up @@ -41,8 +41,9 @@ defmodule Req.Test.Ownership do
GenServer.start_link(__MODULE__, [], genserver_opts)
end

@spec allow(server(), pid(), pid() | (-> pid()), key()) ::
@spec allow(server(), pid(), pid() | (-> resolved_pid), key()) ::
:ok | {:error, Error.t()}
when resolved_pid: pid() | [pid()]
def allow(ownership_server, pid_with_access, pid_to_allow, key, timeout \\ 5000)
when is_pid(pid_with_access) and (is_pid(pid_to_allow) or is_function(pid_to_allow, 0)) and
is_timeout(timeout) do
Expand Down Expand Up @@ -117,10 +118,7 @@ defmodule Req.Test.Ownership do
allowances: %{},

# This is used to track which PIDs we're monitoring, to avoid double-monitoring.
monitored_pids: MapSet.new(),

# This boolean field tracks whether there are any lazy calls in the allowances.
lazy_calls: false
monitored_pids: MapSet.new()
]

## Callbacks
Expand Down Expand Up @@ -180,15 +178,12 @@ defmodule Req.Test.Ownership do
state
|> maybe_monitor_pid(pid_with_access)
|> put_in([Access.key!(:allowances), Access.key(pid_to_allow, %{}), key], owner_pid)
|> update_in([Access.key!(:lazy_calls)], &(&1 or is_function(pid_to_allow, 0)))

{:reply, :ok, state}
end
end

def handle_call({:get_and_update, owner_pid, key, fun}, _from, %__MODULE__{} = state) do
state = revalidate_lazy_calls(state)

case state.mode do
{:shared, shared_owner_pid} when shared_owner_pid != owner_pid ->
error = %Error{key: key, reason: {:not_shared_owner, shared_owner_pid}}
Expand All @@ -198,6 +193,8 @@ defmodule Req.Test.Ownership do
:ok
end

state = resolve_lazy_calls_for_key(state, key)

if other_owner = state.allowances[owner_pid][key] do
throw({:reply, {:error, %Error{key: key, reason: {:already_allowed, other_owner}}}, state})
end
Expand Down Expand Up @@ -238,15 +235,21 @@ defmodule Req.Test.Ownership do
end

def handle_call({:fetch_owner, callers, key}, _from, %__MODULE__{mode: :private} = state) do
state = revalidate_lazy_calls(state)

Enum.find_value(callers, {:reply, :error, state}, fn caller ->
cond do
owner_pid = state.allowances[caller][key] -> {:reply, {:ok, owner_pid}, state}
_meta = state.owners[caller][key] -> {:reply, {:ok, caller}, state}
true -> nil
{owner, state} =
case fetch_owner_once(state, callers, key) do
nil ->
state = resolve_lazy_calls_for_key(state, key)
{fetch_owner_once(state, callers, key), state}

owner ->
{owner, state}
end
end)

if is_nil(owner) do
{:reply, :error, state}
else
{:reply, {:ok, owner}, state}
end
end

def handle_call({:get_owned, owner_pid, default}, _from, %__MODULE__{} = state) do
Expand Down Expand Up @@ -328,51 +331,38 @@ defmodule Req.Test.Ownership do
end
end

defp revalidate_lazy_calls(state) do
state.allowances
|> Enum.reduce({[], [], false}, fn
{key, value}, {result, resolved, unresolved} when is_function(key, 0) ->
resolve_once(key.(), {key, value}, {result, resolved, unresolved})

kv, {result, resolved, unresolved} ->
{[kv | result], resolved, unresolved}
defp fetch_owner_once(state, callers, key) do
Enum.find_value(callers, fn caller ->
case state do
%{owners: %{^caller => %{^key => _meta}}} -> caller
%{allowances: %{^caller => %{^key => owner_pid}}} -> owner_pid
_ -> nil
end
end)
|> fix_resolved(state)
end

defp resolve_once(pid, {key, value}, {result, resolved, unresolved}) when is_pid(pid) do
{[{pid, value} | result], [{key, pid} | resolved], unresolved}
end

defp resolve_once([pid | pids], {key, value}, {result, resolved, unresolved})
when is_pid(pid) do
resolve_once(
pids,
{key, value},
{[{pid, value} | result], [{key, pid} | resolved], unresolved}
)
end

defp resolve_once([_not_a_pid | pids], kv, {result, resolved, _unresolved}) do
resolve_once(pids, kv, {[kv | result], resolved, true})
end
defp resolve_lazy_calls_for_key(state, key) do
updated_allowances =
Enum.reduce(state.allowances, state.allowances, fn
{fun, value}, allowances when is_function(fun, 0) and is_map_key(value, key) ->
result =
fun.()
|> List.wrap()
|> Enum.group_by(&is_pid/1)

defp resolve_once([], _kv, {result, resolved, unresolved}) do
{result, resolved, unresolved}
end
allowances =
result
|> Map.get(true, [])
|> Enum.reduce(allowances, fn pid, allowances ->
Map.update(allowances, pid, value, &Map.merge(&1, value))
end)

defp resolve_once(_, kv, {result, resolved, _unresolved}) do
{[kv | result], resolved, true}
end
if Map.has_key?(allowances, false), do: Map.delete(allowances, fun), else: allowances

defp fix_resolved({_, [], _}, state), do: state

defp fix_resolved({allowances, _fun_to_pids, lazy_calls}, state) do
allowances =
Enum.reduce(allowances, %{}, fn {k, v}, acc ->
Map.update(acc, k, v, &Map.merge(&1, v))
_, allowances ->
allowances
end)

%__MODULE__{state | allowances: allowances, lazy_calls: lazy_calls}
%{state | allowances: updated_allowances}
end
end
12 changes: 0 additions & 12 deletions lib/req/test/ownership_error.ex
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
# Vendored from nimble_ownership. See Req.Test.Ownership.
defmodule Req.Test.OwnershipError do
@moduledoc false

@type t() :: %__MODULE__{
reason:
{:already_allowed, pid()}
| :not_allowed
| :already_an_owner
| :cant_allow_in_shared_mode
| {:not_shared_owner, pid()},
key: Req.Test.Ownership.key()
}

defexception [:reason, :key]

@impl true
Expand Down

0 comments on commit d3a6727

Please sign in to comment.