diff --git a/lib/trinity/scheduler.ex b/lib/trinity/scheduler.ex index 42a05ad..3a86b39 100644 --- a/lib/trinity/scheduler.ex +++ b/lib/trinity/scheduler.ex @@ -258,6 +258,27 @@ defmodule Trinity.Scheduler do :ok end + def handle_kill_node(node, from_pid) do + %{node_procs: node_procs} = sim = get_sim() + [{_node, pids}] = :ets.lookup(node_procs, node) + dbg pids + + # Note: we can ignore links here because we currently only support links on the same node + Enum.each(pids, fn pid -> + # Killing a node from within the node is not currently allowed + # TODO: maybe allow this? + assert pid != from_pid + destroy_process(sim, pid) + + assert Process.alive?(pid) + Process.exit(pid, :kill) + + receive do + {:EXIT, ^pid, _reason} -> :noop + end + end) + end + defp destroy_or_message_linked(%Simulation{} = sim, for_pid, reason, visited) do linked = case :ets.lookup(sim.proc_links, for_pid) do diff --git a/lib/trinity/scheduler/simulation_supervisor.ex b/lib/trinity/scheduler/simulation_supervisor.ex index e9bd0a6..8348e9a 100644 --- a/lib/trinity/scheduler/simulation_supervisor.ex +++ b/lib/trinity/scheduler/simulation_supervisor.ex @@ -39,6 +39,10 @@ defmodule Trinity.Scheduler.SimulationSupervisor do GenServer.call(server, {:spawn_child, fun}) end + def kill_node(server, node) do + GenServer.call(server, {:kill_node, node, self()}) + end + def init(%{seed: seed, fun: fun, parent_pid: parent_pid, ref: ref}) do Process.flag(:trap_exit, true) :rand.seed(:exsss, seed) @@ -91,6 +95,11 @@ defmodule Trinity.Scheduler.SimulationSupervisor do {:reply, pid, state} end + def handle_call({:kill_node, node, from_pid}, _from, %State{} = state) do + Trinity.Scheduler.handle_kill_node(node, from_pid) + {:reply, :ok, state} + end + def handle_info({ref, result}, %State{} = state) when ref == state.ref do {:noreply, %{state | result: result}} end diff --git a/lib/trinity/sim.ex b/lib/trinity/sim.ex index 18ec452..ff19bf2 100644 --- a/lib/trinity/sim.ex +++ b/lib/trinity/sim.ex @@ -12,6 +12,22 @@ defmodule Trinity.Sim do Trinity.Scheduler.SimulationSupervisor.run_simulation(fun, %{seed: seed}) end + @spec list_nodes :: [atom] + def list_nodes do + %{node_procs: node_procs} = get_sim() + :ets.tab2list(node_procs) + |> Enum.map(fn {node, _pids} -> node end) + |> Enum.reject(&(&1 == :nonode)) + |> Enum.sort() + end + + @spec kill_node(atom) :: :ok + def kill_node(node) do + %{supervisor_pid: supervisor_pid} = get_sim() + Trinity.Scheduler.SimulationSupervisor.kill_node(supervisor_pid, node) + :ok + end + @spec simulated? :: boolean def simulated? do case get_sim() do