diff --git a/lib/livebook/runtime.ex b/lib/livebook/runtime.ex
index 9f90f2462..34162116e 100644
--- a/lib/livebook/runtime.ex
+++ b/lib/livebook/runtime.ex
@@ -199,4 +199,15 @@ defprotocol Livebook.Runtime do
"""
@spec duplicate(Runtime.t()) :: {:ok, Runtime.t()} | {:error, String.t()}
def duplicate(runtime)
+
+ @doc """
+ Returns true if the given runtime is self-contained.
+
+ A standalone runtime always starts fresh and frees all
+ resources when terminated. This may not be the case for
+ for runtimes that connect to an external running system
+ and use it for code evaluation.
+ """
+ @spec standalone?(Runtime.t()) :: boolean()
+ def standalone?(runtime)
end
diff --git a/lib/livebook/runtime/attached.ex b/lib/livebook/runtime/attached.ex
index 2b65d280b..4d7ebb320 100644
--- a/lib/livebook/runtime/attached.ex
+++ b/lib/livebook/runtime/attached.ex
@@ -71,4 +71,6 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Attached do
{:error, :unreachable} -> {:error, "node #{inspect(runtime.node)} is unreachable"}
end
end
+
+ def standalone?(_runtime), do: false
end
diff --git a/lib/livebook/runtime/elixir_standalone.ex b/lib/livebook/runtime/elixir_standalone.ex
index 4958cb196..ec95ee133 100644
--- a/lib/livebook/runtime/elixir_standalone.ex
+++ b/lib/livebook/runtime/elixir_standalone.ex
@@ -97,4 +97,6 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.ElixirStandalone do
def duplicate(_runtime) do
Livebook.Runtime.ElixirStandalone.init()
end
+
+ def standalone?(_runtime), do: true
end
diff --git a/lib/livebook/runtime/embedded.ex b/lib/livebook/runtime/embedded.ex
index 1ce38edbc..06fed0fa9 100644
--- a/lib/livebook/runtime/embedded.ex
+++ b/lib/livebook/runtime/embedded.ex
@@ -70,4 +70,6 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.Embedded do
def duplicate(_runtime) do
Livebook.Runtime.Embedded.init()
end
+
+ def standalone?(_runtime), do: false
end
diff --git a/lib/livebook/runtime/mix_standalone.ex b/lib/livebook/runtime/mix_standalone.ex
index 506fdbf2b..d54ebb49f 100644
--- a/lib/livebook/runtime/mix_standalone.ex
+++ b/lib/livebook/runtime/mix_standalone.ex
@@ -150,4 +150,6 @@ defimpl Livebook.Runtime, for: Livebook.Runtime.MixStandalone do
def duplicate(runtime) do
Livebook.Runtime.MixStandalone.init(runtime.project_path)
end
+
+ def standalone?(_runtime), do: true
end
diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex
index b803cdec5..42f2f59ee 100644
--- a/lib/livebook_web/live/session_live.ex
+++ b/lib/livebook_web/live/session_live.ex
@@ -240,6 +240,7 @@ defmodule LivebookWeb.SessionLive do
id: section_view.id,
index: index,
session_id: @session.id,
+ runtime: @data_view.runtime,
section_view: section_view %>
<% end %>
diff --git a/lib/livebook_web/live/session_live/attached_live.ex b/lib/livebook_web/live/session_live/attached_live.ex
index c77cbe852..41675fe68 100644
--- a/lib/livebook_web/live/session_live/attached_live.ex
+++ b/lib/livebook_web/live/session_live/attached_live.ex
@@ -2,12 +2,18 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
use LivebookWeb, :live_view
alias Livebook.{Session, Runtime, Utils}
+ alias LivebookWeb.SessionLive.RuntimeHelpers
@impl true
def mount(_params, %{"session" => session, "current_runtime" => current_runtime}, socket) do
+ if connected?(socket) do
+ Phoenix.PubSub.subscribe(Livebook.PubSub, "sessions:#{session.id}")
+ end
+
{:ok,
assign(socket,
session: session,
+ current_runtime: current_runtime,
error_message: nil,
data: initial_data(current_runtime)
)}
@@ -22,6 +28,7 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
<%= @error_message %>
<% end %>
+
Connect the session to an already running node
and evaluate code in the context of that node.
@@ -57,13 +64,19 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
"""
end
+ defp matching_runtime?(%Runtime.Attached{} = runtime, data) do
+ initial_data(runtime) == data
+ end
+
+ defp matching_runtime?(_runtime, _data), do: false
+
@impl true
def handle_event("validate", %{"data" => data}, socket) do
{:noreply, assign(socket, data: data)}
@@ -76,7 +89,7 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
case Runtime.Attached.init(node, cookie) do
{:ok, runtime} ->
Session.connect_runtime(socket.assigns.session.pid, runtime)
- {:noreply, assign(socket, data: data, error_message: nil)}
+ {:noreply, assign(socket, data: initial_data(runtime), error_message: nil)}
{:error, error} ->
message = runtime_error_to_message(error)
@@ -84,6 +97,13 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
end
end
+ @impl true
+ def handle_info({:operation, {:set_runtime, _pid, runtime}}, socket) do
+ {:noreply, assign(socket, current_runtime: runtime)}
+ end
+
+ def handle_info(_message, socket), do: {:noreply, socket}
+
defp initial_data(%Runtime.Attached{node: node, cookie: cookie}) do
%{
"name" => Atom.to_string(node),
diff --git a/lib/livebook_web/live/session_live/cell_component.ex b/lib/livebook_web/live/session_live/cell_component.ex
index d37c3a6d0..c8b239de2 100644
--- a/lib/livebook_web/live/session_live/cell_component.ex
+++ b/lib/livebook_web/live/session_live/cell_component.ex
@@ -99,7 +99,7 @@ defmodule LivebookWeb.SessionLive.CellComponent do
<%= if @cell_view.outputs != [] do %>
- <.outputs cell_view={@cell_view} socket={@socket} />
+ <.outputs cell_view={@cell_view} runtime={@runtime} socket={@socket} />
<% end %>
@@ -431,7 +431,8 @@ defmodule LivebookWeb.SessionLive.CellComponent do
<%= render_output(output, %{
id: "cell-#{@cell_view.id}-evaluation#{evaluation_number(@cell_view.evaluation_status, @cell_view.number_of_evaluations)}-output#{index}",
- socket: @socket
+ socket: @socket,
+ runtime: @runtime
}) %>
<% end %>
@@ -483,17 +484,26 @@ defmodule LivebookWeb.SessionLive.CellComponent do
)
end
- defp render_output({:error, formatted, :runtime_restart_required}, %{}) do
- assigns = %{formatted: formatted}
+ defp render_output({:error, formatted, :runtime_restart_required}, %{runtime: runtime})
+ when runtime != nil do
+ assigns = %{formatted: formatted, is_standalone: Livebook.Runtime.standalone?(runtime)}
~H"""
<%= render_error_message_output(@formatted) %>
-
-
-
+ <%= if @is_standalone do %>
+
+
+
+ <% else %>
+
+ Note:
+ This operation requires restarting the runtime, but we cannot
+ do it automatically for the current runtime
+
+ <% end %>
"""
end
diff --git a/lib/livebook_web/live/session_live/elixir_standalone_live.ex b/lib/livebook_web/live/session_live/elixir_standalone_live.ex
index d6853cd14..ea307f066 100644
--- a/lib/livebook_web/live/session_live/elixir_standalone_live.ex
+++ b/lib/livebook_web/live/session_live/elixir_standalone_live.ex
@@ -2,6 +2,7 @@ defmodule LivebookWeb.SessionLive.ElixirStandaloneLive do
use LivebookWeb, :live_view
alias Livebook.{Session, Runtime}
+ alias LivebookWeb.SessionLive.RuntimeHelpers
@impl true
def mount(_params, %{"session" => session, "current_runtime" => current_runtime}, socket) do
@@ -21,10 +22,9 @@ defmodule LivebookWeb.SessionLive.ElixirStandaloneLive do
<%= @error_message %>
<% end %>
+
Start a new local node to handle code evaluation.
- This is the default runtime and is started automatically
- as soon as you evaluate the first cell.