Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix several allowances with same keys #6

Merged
merged 2 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/nimble_ownership.ex
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,11 @@ defmodule NimbleOwnership do
defp fix_resolved({_, [], _}, state), do: state

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

%__MODULE__{state | allowances: allowances, lazy_calls: lazy_calls}
end
end
64 changes: 64 additions & 0 deletions test/nimble_ownership_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,70 @@ defmodule NimbleOwnershipTest do
assert get_meta(self(), key) == %{counter: 2}
end

test "properly merges lazy allowed PIDs that resolve on the next upsert", %{key: key} do
parent_pid = self()

parent_process_fun = fn counter ->
fn ->
# Needed to trick double allowance checker
Process.delete(:"$callers")

# Init the key
key = "#{counter} → #{key}"
init_key(self(), key, %{counter: 1})

# Allow two lazy PID that will resolve later
assert :ok =
NimbleOwnership.allow(
@server,
self(),
fn -> Process.whereis(:lazy_pid) end,
key
)

receive do
{:go, lazy_pid} ->
send(lazy_pid, {:go, self(), key})
assert_receive :done

assert NimbleOwnership.fetch_owner(@server, [self()], key) == {:ok, self()}
assert get_meta(self(), key) == %{counter: 2}

send(parent_pid, :parent_process_done)
end
end
end

{:ok, pid_1} = Task.start_link(parent_process_fun.(1))
{:ok, pid_2} = Task.start_link(parent_process_fun.(2))

lazy_process_fun = fn ->
for _ <- 1..2 do
receive do
{:go, parent_pid, key} ->
assert {:ok, owner_pid} = NimbleOwnership.fetch_owner(@server, [parent_pid], key)
assert owner_pid == parent_pid

NimbleOwnership.get_and_update(@server, owner_pid, key, fn info ->
assert info == %{counter: 1}
{:ok, %{counter: 2}}
end)

send(parent_pid, :done)
end
end
end

{:ok, lazy_pid} = Task.start_link(lazy_process_fun)
Process.register(lazy_pid, :lazy_pid)

send(pid_1, {:go, lazy_pid})
send(pid_2, {:go, lazy_pid})

assert_receive :parent_process_done
assert_receive :parent_process_done
end

test "ignores lazy PIDs that don't actually resolve to a PID", %{key: key} do
owner_pid = self()

Expand Down
Loading