How does message passing in Processes work?

Trying to wrap my head around how processes pass messages. I have a simple example of processA sending a message to processB

defmodule ProcessA do
  def start() do
    spawn fn -> run() end
  end

  def run do
    run()
  end

end
defmodule ProcessB do
  def start do
    spawn fn -> run() end
  end

  def run do
    run()
  end

end
iex(1)> process_a = ProcessA.start
#PID<0.153.0>
iex(2)> process_b = ProcessB.start
#PID<0.154.0>
iex(3)> send(process_b, "Hello")
"Hello"
iex(4)> Process.info(process_b, :messages)
{:messages, ["Hello"]}

When I use the send function the Iex process is sending a message to process_b.

I don’t understand how process_a can send a message to process_b.

Every process can send another process a message if it knows how to reference it. Obviously that’s not doing being done willy-nilly but it’s a feature that is widely used and is a core tenet of the OTP.

You’ll have to dive into the Erlang source code if you want to see the guts of the function!

Yup!

You can send a message to process_a that tells it to send a message to process_b.

Here’s a quick example where the iex process sends a message to process_a which then sends that message to process_b along with the iex process pid so that process_b can notify the iex process that the message was received.

defmodule ProcessA do
  def start() do
    spawn fn -> run() end
  end

  def run do
    receive do
        {:send, recipient_pid, {origin_pid, message}} -> send(recipient_pid, {origin_pid, message})
    end
    run()
  end
end

defmodule ProcessB do
  def start do
    spawn fn -> run() end
  end

  def run do
    receive do
        {origin_pid, message} -> send(origin_pid, {self(), message})
    end
    run()
  end
end

iex(4)> iex_pid = self()
#PID<0.113.0>
iex(5)> process_a = ProcessA.start
#PID<0.134.0>
iex(6)> process_b = ProcessB.start
#PID<0.135.0>
iex(7)> send(process_a, {:send, process_b, {iex_pid, "hello world"}})
{:send, #PID<0.135.0>, {#PID<0.113.0>, "hello world"}}
iex(8)> flush
{#PID<0.135.0>, "hello world"}
:ok
2 Likes

I’m not sure but based on your naming scheme I think you may be conflating modules and processes. This is a fairly common mistake. A process is a distinct, self-contained concept that runs code and stores state whereas modules are merely static bags of functions that can be call from any process.

3 Likes

Every process has a pid, if you know this pid you can send a message to it.
That is what you are doing when you write process_b = ProcessB.start.
You are saving the pid in the variable process_b.
There are several methods that allow you to make the pid of one process available to another, for example using the Registry.