Skip to content

Instantly share code, notes, and snippets.

@hugobarauna
Last active June 13, 2024 13:37
Show Gist options
  • Save hugobarauna/6dd8b05b51d66bf313c3870c2bc428ed to your computer and use it in GitHub Desktop.
Save hugobarauna/6dd8b05b51d66bf313c3870c2bc428ed to your computer and use it in GitHub Desktop.
Process Labels demo

Process labels demo

Mix.install([
  {:kino, github: "livebook-dev/kino", ref: "hb-support-process-labels"}
])

Kino.Process.render_seq_trace

Ping pong default

parent = self()

Kino.Process.render_seq_trace(fn ->
  child =
    spawn(fn ->
      Process.set_label(:ponger)

      receive do
        :ping ->
          # Process.sleep(1)
          send(parent, :pong)
      end
    end)

  send(child, :ping)

  receive do
    :pong -> :ponged!
  end
end)

Ping pong with ponger outside traced function

parent = self()

child =
  spawn(fn ->
    Process.set_label(:ponger)

    receive do
      :ping ->
        Process.sleep(1) # theres's a race condition here
        send(parent, :pong)
    end
  end)

Kino.Process.render_seq_trace(fn ->
  send(child, :ping)

  receive do
    :pong -> :ponged!
  end
end)

Ping pong with ponger as genserver (long living process)

defmodule Worker do
  use GenServer

  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts)
  end

  @impl true
  def init(opts) do
    if label = Keyword.get(opts, :label) do
      Process.set_label(label)
    end

    {:ok, opts}
  end

  @impl true
  def handle_info({:ping, from}, opts) do
    send(from, :pong)
    {:noreply, opts}
  end
end

worker = Kino.start_child!({Worker, [label: "my worker"]})

Kino.Process.render_seq_trace(fn ->
  send(worker, {:ping, self()})

  receive do
    :pong -> :ponged!
  end
end)

Ping pong with ponger as task with receive loop

parent = self()
process_label = "ponger"

ponger_pid =
  Task.async(fn ->
    Process.set_label(process_label)

    receive_loop = fn f ->
      receive do
        :ping ->
          send(parent, :pong)
          f.(f)
      end
    end

    receive_loop.(receive_loop)
  end).pid

trace_function = fn ->
  send(ponger_pid, :ping)

  receive do
    :pong -> :ponged!
  end
end

# trace_function.()
Kino.Process.render_seq_trace(trace_function)

Kino.Process.render_sup_tree

# Stop the supervisor if it is already running
if is_pid(Process.whereis(:supervisor_parent)) do
  Supervisor.stop(:supervisor_parent)
end

supervision_tree_with_children = %{
  id: Supervisor,
  start:
    {Supervisor, :start_link,
     [
       [
         %{id: :child, start: {Agent, :start_link, [fn -> :ok end, [name: :agent_child]]}},
         %{id: 1, start: {Worker, :start_link, [[label: {"tuple label", 2}]]}},
         %{id: 2, start: {Worker, :start_link, [[label: :atom_label]]}},
         %{id: 3, start: {Worker, :start_link, [[label: "string label"]]}},
         %{id: 4, start: {Worker, :start_link, [[]]}}
       ],
       [name: :supervisor_parent, strategy: :one_for_one]
     ]},
  restart: :temporary
}

pid = Kino.start_child!(supervision_tree_with_children)
Kino.Process.sup_tree(pid)

With ETS table

supervision_tree_with_ets_table = %{
  id: Supervisor,
  start:
    {Supervisor, :start_link,
     [
       [
         {Agent, fn -> :ok end},
         %{
           id: :ets_heir,
           start: {Agent, :start_link, [fn -> :ok end, [name: :ets_heir]]}
         },
         %{
           id: :ets_owner,
           start:
             {Agent, :start_link,
              [
                fn ->
                  heir_pid = Process.whereis(:ets_heir)

                  :ets.new(
                    :test_ets_table,
                    [:set, :protected, :named_table, {:heir, heir_pid, nil}]
                  )
                end,
                [name: :ets_owner]
              ]}
         },
        %{id: 11, start: {Worker, :start_link, [[label: {"tuple label", 2}]]}},
       ],
       [name: :supervisor_parent_ets, strategy: :one_for_one]
     ]},
  restart: :temporary
}

pid_with_ets = Kino.start_child!(supervision_tree_with_ets_table)
Kino.Process.sup_tree(pid_with_ets, render_ets_tables: true)

With not started processes

defmodule TransientWorker do
  use GenServer

  def start_link(opts) do
    GenServer.start_link(__MODULE__, opts)
  end

  @impl true
  def init(opts) do
    if label = Keyword.get(opts, :label) do
      Process.set_label(label)
    end

    :ignore
    # {:ok, opts}
  end
end

# Stop the supervisor if it is already running
if is_pid(Process.whereis(:supervisor_parent_transiet_worker)) do
  Supervisor.stop(:supervisor_parent_transiet_worker)
end

supervision_tree_with_children = %{
  id: Supervisor,
  start:
    {Supervisor, :start_link,
     [
       [
         %{id: 11, start: {Worker, :start_link, [[label: {"tuple label", 2}]]}},
         %{id: 12, start: {Worker, :start_link, [[label: :atom_label]]}},
         # %{id: 13, start: {TransientWorker, :start_link, [[label: "string label"]]}}
         {TransientWorker, [label: "string label"]}
       ],
       [name: :supervisor_parent_transiet_worker, strategy: :one_for_one]
     ]},
  restart: :temporary
}

pid = Kino.start_child!(supervision_tree_with_children)
Kino.Process.sup_tree(pid)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment