diff --git a/lib/livebook/runtime/erl_dist/runtime_server.ex b/lib/livebook/runtime/erl_dist/runtime_server.ex index 1082c39e1..d55cbb8c3 100644 --- a/lib/livebook/runtime/erl_dist/runtime_server.ex +++ b/lib/livebook/runtime/erl_dist/runtime_server.ex @@ -576,6 +576,7 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do put_in(state.smart_cells[ref], info) {:error, error} -> + send(state.owner, {:runtime_smart_cell_down, ref}) Logger.error("failed to start smart cell - #{Exception.format_exit(error)}") state end @@ -594,10 +595,10 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServer do end def handle_cast({:stop_smart_cell, ref}, state) do - {%{pid: pid}, state} = pop_in(state.smart_cells[ref]) + {info, state} = pop_in(state.smart_cells[ref]) - if pid do - DynamicSupervisor.terminate_child(state.smart_cell_supervisor, pid) + if info do + DynamicSupervisor.terminate_child(state.smart_cell_supervisor, info.pid) end {:noreply, state} diff --git a/test/livebook/runtime/erl_dist/runtime_server_test.exs b/test/livebook/runtime/erl_dist/runtime_server_test.exs index 92dddd8ef..bfb77efcb 100644 --- a/test/livebook/runtime/erl_dist/runtime_server_test.exs +++ b/test/livebook/runtime/erl_dist/runtime_server_test.exs @@ -242,6 +242,10 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do @impl true def init(info) do + if info.attrs["crash"] do + raise "crash" + end + {:ok, info} end @@ -274,6 +278,13 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do assert_receive {:runtime_smart_cell_down, "ref"} end + @tag opts: @opts + @tag capture_log: true + test "notifies runtime owner when a smart cell fails to start", %{pid: pid} do + RuntimeServer.start_smart_cell(pid, "dumb", "ref", %{"crash" => true}, []) + assert_receive {:runtime_smart_cell_down, "ref"} + end + @tag opts: @opts test "once started scans binding and sends the result to the cell server", %{pid: pid} do RuntimeServer.start_smart_cell(pid, "dumb", "ref", %{}, []) @@ -304,5 +315,17 @@ defmodule Livebook.Runtime.ErlDist.RuntimeServerTest do RuntimeServer.evaluate_code(pid, :elixir, "1 + 1", {:c1, :e1}, [], smart_cell_ref: "ref") assert_receive {:smart_cell_debug, "ref", :handle_info, :scan_eval_result_ping} end + + @tag opts: @opts + test "ignores stop request for stopped smart cells", %{pid: pid} do + RuntimeServer.start_smart_cell(pid, "dumb", "ref", %{}, []) + assert_receive {:runtime_smart_cell_started, "ref", %{js_view: %{pid: smart_cell_pid}}} + Process.exit(smart_cell_pid, :crashed) + assert_receive {:runtime_smart_cell_down, "ref"} + RuntimeServer.stop_smart_cell(pid, "ref") + + # The server should still be operational + RuntimeServer.evaluate_code(pid, :elixir, "1 + 1", {:c1, :e1}, []) + end end end