Current section
Files
Jump to
Current section
Files
lib/node/ports/port.ex
defmodule ExUnit.ClusteredCase.Node.Ports.Port do
@moduledoc false
require Logger
def child_spec(args) do
%{id: __MODULE__, type: :worker, start: {__MODULE__, :start_link, [args]}}
end
def start_link([owner_pid, opts, owner_opts]) do
:proc_lib.start_link(__MODULE__, :init, [self(), owner_pid, opts, owner_opts])
end
def init(parent, owner, opts, owner_opts) do
Process.flag(:trap_exit, true)
debug = :sys.debug_options([])
port = open_port(opts)
if Keyword.get(owner_opts, :link, false) do
Process.link(owner)
end
:proc_lib.init_ack(parent, {:ok, self()})
loop(parent, debug, port, owner, opts)
end
defp open_port(opts) do
# Open port
erl = System.find_executable("erl")
port_opts = [:stream | [env: opts.env, args: opts.erl_flags]]
Port.open({:spawn_executable, erl}, port_opts)
end
defp loop(parent, debug, port, owner, opts) do
name = opts.name
heart? = opts.heart
receive do
{:system, from, req} ->
:sys.handle_system_msg(req, from, parent, __MODULE__, debug, {port, owner, opts})
{:EXIT, ^parent, reason} ->
exit(reason)
{:EXIT, ^port, _reason} when heart? ->
# If port closes and heart option is active, restart node
port = open_port(opts)
loop(parent, debug, port, owner, opts)
{:EXIT, ^port, reason} ->
# If heart option is not active, terminate when port closes
exit(reason)
{:EXIT, ^owner, reason} ->
# If the owning process terminates, we terminate too
exit(reason)
{^port, {:data, data}} ->
# We're receiving logged output from the port, relay it
IO.puts(["#{name}: ", data])
loop(parent, debug, port, owner, opts)
msg ->
Logger.warn("Unexpected message received by #{__MODULE__} for #{name}: #{inspect(msg)}")
loop(parent, debug, port, owner, opts)
end
end
# :sys callbacks
@doc false
def system_continue(parent, debug, {port, owner, opts}) do
loop(parent, debug, port, owner, opts)
end
@doc false
def system_get_state(state), do: {:ok, state}
@doc false
def system_replace_state(fun, state) do
new_state = fun.(state)
{:ok, new_state, new_state}
end
@doc false
def system_code_change(state, _mod, _old, _extra) do
{:ok, state}
end
@doc false
def system_terminate(reason, _parent, _debug, _state) do
reason
end
end