Powered by AppSignal & Oban Pro

Untitled notebook

01-process-basic.livemd

Untitled notebook

Mix.install([
  {:kino, "~> 0.9.2"}
])

PIDs

self()

Spawning

To create a new process, we can use spawn/1:

spawn(fn ->
  Process.sleep(1000)
  IO.puts("PID #{inspect(self())} is done")
end)

IO.puts("Spawned a process from #{inspect(self())}")

Sending and Receiving Messages

  • Processes communicate via messages
  • A process can send or receive messages
  • Similar guarantees to TCP
    • Messages are delivered in the same order they are sent
    • All messages are eventually delivered if the destination process stays alive
    • No guarantee about what the destination process does with the message
send(self(), :hello)
receive do
  message -> message
end
receive do
  message -> message
after
  1000 -> :timeout
end
send(self(), 102)

# Complete: with timeout and different clauses
receive do
  %{} -> :map
  number when is_number(number) -> :number
  _other -> :other
after
  1000 -> :timeout
end
Kino.Process.render_seq_trace(fn ->
  pid =
    spawn(fn ->
      type =
        receive do
          %{} -> :map
          number when is_number(number) -> :number
          _other -> :other
        after
          1000 -> :timeout
        end

      IO.puts("Type is #{inspect(type)}")
    end)

  send(pid, 123)
end)

Parallel Map

defmodule ParallelEnum do
  def map(enum, fun) when is_function(fun, 1) do
    raise "not implemented yet"
  end
end
ExUnit.start()
defmodule ParallelEnumTests do
  use ExUnit.Case

  test "works like Enum.map/1" do
    assert ParallelEnum.map(1..4, &Integer.to_string/1) == ["1", "2", "3", "4"]
  end

  test "work in parallel " do
    {elapsed, _result} =
      :timer.tc(fn ->
        ParallelEnum.map(1..4, fn _ -> Process.sleep(500) end)
      end)

    assert elapsed < 600
  end
end

ExUnit.run()
Kino.Process.render_seq_trace(fn ->
  1..3
  |> Task.async_stream(fn _ ->
    Process.sleep(Enum.random(50..200))
  end)
  |> Stream.run()
end)

Links and Monitors

Kino.Process.render_seq_trace(fn ->
  crasher_pid =
    spawn(fn ->
      Process.sleep(1000)
      raise "I crash 👹"
    end)

  linked_pid =
    spawn(fn ->
      Process.link(crasher_pid)
      Process.sleep(:infinity)
    end)

  Process.sleep(1100)
  Process.alive?(linked_pid)
end)
pid =
  spawn(fn ->
    Process.sleep(500)
    :i_finished_just_fine
  end)

ref = Process.monitor(pid)

receive do
  {:DOWN, ^ref, _type, _pid, _reason} = down_message ->
    down_message
end