Packages
kino
0.11.2
0.19.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.3
0.15.2
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.2
0.13.1
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.0
0.6.2
0.6.1
0.6.0
retired
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Interactive widgets for Livebook
Current section
Files
Jump to
Current section
Files
lib/kino/terminator.ex
defmodule Kino.Terminator do
@moduledoc false
# A process responsible for shutting down processes.
use GenServer
@name __MODULE__
def cross_node_name() do
{@name, node()}
end
@doc """
Starts a Kino process to be shutdown by the terminator.
"""
def start_child({mod, fun, args}, parent, gl) do
# We switch the group leader, so that the newly started
# process gets the same group leader as the caller
initial_gl = Process.group_leader()
Process.group_leader(self(), gl)
try do
{resp, pid} =
case apply(mod, fun, args) do
{:ok, pid} = resp -> {resp, pid}
{:ok, pid, _info} = resp -> {resp, pid}
resp -> {resp, nil}
end
if pid do
Kino.Bridge.reference_object(pid, parent)
Kino.Bridge.monitor_object(pid, cross_node_name(), {:terminate, pid}, ack?: true)
end
resp
after
Process.group_leader(self(), initial_gl)
end
end
@doc """
Starts a task that will terminate the parent in case of crashes.
"""
def start_task(parent, fun) do
Task.start_link(fn ->
GenServer.call(@name, {:monitor, self(), parent})
fun.()
end)
end
@doc """
Starts the terminator.
"""
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts, name: @name)
end
@impl true
def init(_opts) do
{:ok, %{}}
end
@impl true
def handle_call({:monitor, pid, parent}, _from, state) do
_ref = Process.monitor(pid)
{:reply, :ok, Map.put(state, pid, parent)}
end
@impl true
def handle_info({{:terminate, pid}, reply_to, reply_as}, state) do
DynamicSupervisor.terminate_child(Kino.DynamicSupervisor, pid)
send(reply_to, reply_as)
{:noreply, Map.delete(state, pid)}
end
@impl true
def handle_info({:DOWN, _, _, pid, reason}, state) do
{parent, state} = Map.pop(state, pid)
if is_pid(parent) and abnormal?(reason) do
Process.exit(parent, reason)
end
{:noreply, state}
end
defp abnormal?(:normal), do: false
defp abnormal?(:shutdown), do: false
defp abnormal?({:shutdown, _}), do: false
defp abnormal?(_), do: true
end