diff --git a/lib/livebook/runtime/erl_dist.ex b/lib/livebook/runtime/erl_dist.ex index 1e6fec13c..211ac2f0d 100644 --- a/lib/livebook/runtime/erl_dist.ex +++ b/lib/livebook/runtime/erl_dist.ex @@ -39,6 +39,7 @@ defmodule Livebook.Runtime.ErlDist do Livebook.Runtime.ErlDist.IOForwardGL, Livebook.Runtime.ErlDist.LoggerGLBackend, Livebook.Runtime.ErlDist.LoggerGLHandler, + Livebook.Runtime.ErlDist.Sink, Livebook.Runtime.ErlDist.SmartCellGL ] end diff --git a/lib/livebook/runtime/erl_dist/logger_gl_handler.ex b/lib/livebook/runtime/erl_dist/logger_gl_handler.ex index 64c14ed65..9e4d4414b 100644 --- a/lib/livebook/runtime/erl_dist/logger_gl_handler.ex +++ b/lib/livebook/runtime/erl_dist/logger_gl_handler.ex @@ -13,7 +13,8 @@ defmodule Livebook.Runtime.ErlDist.LoggerGLHandler do end def async_io(device, output) when is_pid(device) do - send(device, {:io_request, self(), make_ref(), {:put_chars, :unicode, output}}) + reply_to = Livebook.Runtime.ErlDist.Sink.pid() + send(device, {:io_request, reply_to, make_ref(), {:put_chars, :unicode, output}}) end @doc false diff --git a/lib/livebook/runtime/erl_dist/node_manager.ex b/lib/livebook/runtime/erl_dist/node_manager.ex index afce5e4ec..b89ed71b0 100644 --- a/lib/livebook/runtime/erl_dist/node_manager.ex +++ b/lib/livebook/runtime/erl_dist/node_manager.ex @@ -98,6 +98,8 @@ defmodule Livebook.Runtime.ErlDist.NodeManager do # TODO: remove logger backend once we require Elixir v1.15 if Code.ensure_loaded?(Logger) and function_exported?(Logger, :add_handlers, 1) do + {:ok, _pid} = Livebook.Runtime.ErlDist.Sink.start_link() + :logger.add_handler(:livebook_gl_handler, Livebook.Runtime.ErlDist.LoggerGLHandler, %{ formatter: Logger.Formatter.new(), filters: [ diff --git a/lib/livebook/runtime/erl_dist/sink.ex b/lib/livebook/runtime/erl_dist/sink.ex new file mode 100644 index 000000000..bf1c7c81a --- /dev/null +++ b/lib/livebook/runtime/erl_dist/sink.ex @@ -0,0 +1,35 @@ +defmodule Livebook.Runtime.ErlDist.Sink do + @moduledoc false + + # An idle process that ignores all incoming messages. + + use GenServer + + @name __MODULE__ + + @doc """ + Starts the process. + """ + @spec start_link() :: GenServer.on_start() + def start_link() do + GenServer.start_link(__MODULE__, {}, name: @name) + end + + @doc """ + Returns pid of the global sink process. + """ + @spec pid() :: pid() + def pid() do + Process.whereis(@name) + end + + @impl true + def init({}) do + {:ok, {}} + end + + @impl true + def handle_info(_message, state) do + {:noreply, state} + end +end