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

Handle Already Dropped Caller #62

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
87 changes: 46 additions & 41 deletions lib/flame/terminator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -330,50 +330,55 @@ defmodule FLAME.Terminator do
end

defp drop_caller(%Terminator{} = state, ref) when is_reference(ref) do
%{^ref => %Caller{} = caller} = state.calls
if caller.timer, do: Process.cancel_timer(caller.timer)
state = %Terminator{state | calls: Map.delete(state.calls, ref)}

# if the caller going down was one that placed a child, and the child is still tracked:
# - if the child is not linked (link: false), do nothing
# - if the child is linked, terminate the child. there is no need to notify the og caller,
# as they linked themselves.
#
# Note: there is also a race where we can't rely on the link to have happened to so we
# must monitor in the terminator even with the remote link
state =
with placed_child_ref <- caller.placed_child_ref,
true <- is_reference(placed_child_ref),
%{^placed_child_ref => %Caller{} = placed_child} <- state.calls,
true <- placed_child.link? do
if placed_child.timer, do: Process.cancel_timer(placed_child.timer)
Process.demonitor(placed_child_ref, [:flush])
DynamicSupervisor.terminate_child(state.child_placement_sup, placed_child.from_pid)
%Terminator{state | calls: Map.delete(state.calls, placed_child_ref)}
else
_ -> state
end
case state.calls do
%{^ref => %Caller{} = caller} ->
if caller.timer, do: Process.cancel_timer(caller.timer)
state = %Terminator{state | calls: Map.delete(state.calls, ref)}

# if the caller going down was one that placed a child, and the child is still tracked:
# - if the child is not linked (link: false), do nothing
# - if the child is linked, terminate the child. there is no need to notify the og caller,
# as they linked themselves.
#
# Note: there is also a race where we can't rely on the link to have happened to so we
# must monitor in the terminator even with the remote link
state =
with placed_child_ref <- caller.placed_child_ref,
true <- is_reference(placed_child_ref),
%{^placed_child_ref => %Caller{} = placed_child} <- state.calls,
true <- placed_child.link? do
if placed_child.timer, do: Process.cancel_timer(placed_child.timer)
Process.demonitor(placed_child_ref, [:flush])
DynamicSupervisor.terminate_child(state.child_placement_sup, placed_child.from_pid)
%Terminator{state | calls: Map.delete(state.calls, placed_child_ref)}
else
_ -> state
end

# if the caller going down was a placed child, clean up the placed caller ref
state =
with placed_caller_ref <- caller.placed_caller_ref,
true <- is_reference(placed_caller_ref),
%{^placed_caller_ref => %Caller{} = placed_caller} <- state.calls do
if placed_caller.timer, do: Process.cancel_timer(placed_caller.timer)
Process.demonitor(placed_caller_ref, [:flush])
%Terminator{state | calls: Map.delete(state.calls, placed_caller_ref)}
else
_ -> state
end
# if the caller going down was a placed child, clean up the placed caller ref
state =
with placed_caller_ref <- caller.placed_caller_ref,
true <- is_reference(placed_caller_ref),
%{^placed_caller_ref => %Caller{} = placed_caller} <- state.calls do
if placed_caller.timer, do: Process.cancel_timer(placed_caller.timer)
Process.demonitor(placed_caller_ref, [:flush])
%Terminator{state | calls: Map.delete(state.calls, placed_caller_ref)}
else
_ -> state
end

state =
if state.single_use do
system_stop(state, "single use completed. Going down")
else
state
end
state =
if state.single_use do
system_stop(state, "single use completed. Going down")
else
state
end

maybe_schedule_shutdown(state)
maybe_schedule_shutdown(state)

_ ->
state
end
end

defp maybe_schedule_shutdown(%{calls: calls, watchers: watchers} = state) do
Expand Down