Is message processing order deterministic or not?

I believe message processing order is not deterministic because any process can run first. However i don’t know why this piece of code is able to produce deterministic order

defmodule MultipleProcesses do
def run(token1, token2) do
pid1 = spawn(MultipleProcesses, :reply, [])
pid2 = spawn(MultipleProcesses, :reply, [])
send pid1, {self, token1}
send pid2, {self, token2}

# ensure token2 is received first, then token1
receive do
  ^token2 -> IO.puts "Got reply: #{token2}"
end
receive do
  ^token1 -> IO.puts "Got reply: #{token1}"
end

end

def reply do
receive do
{sender, token} -> send sender, token
end
end
end

MultipleProcesses.run(“hello”, “world”)

Won’t token1 be dropped when it is being processed by the first receive block?

Messages between two processes are always processed in-order, so that is deterministic.

However, message from multiple processes to a single process can interleave in unexpected ways, that is ‘not’ deterministic, however multiple messages from any single one of those multiple processes will always be in a deterministic order. :slight_smile:

I have no clue what you mean by dropped?

3 Likes

When you pattern match in receive the first matching message is processed and removed from the inbox

Messages will be processed by a process in the order they were received (first-in first-out).

However, when you have multiple processes, and look to your multi-process system from the outside (such as when reading logged messages), then the code in these processes might run in any order, including (partially) in parallel, because they are spread out over your available CPU cores.

Nitpick: Messages sent from A to B will arrive in B’s mailbox in the order that A sent them. But B can choose to process them in any order - and this is what is happening in the OP’s code.

  • receive ^token2 waits until the ^token2 message arrives in the mailbox potentially passing over the ^token1 message.
  • receive ^token1 waits for the ^token1 message (or simply grabs it if it’s already in the mailbox).

No, unprocessed messages remain in the mailbox.

Hence:

Erlang Programming Rules and Conventions: 5.8 Flush unknown messages

Every server should have an Other alternative in at least one receive statement. This is to avoid filling up message queues.

http://www.erlang.se/doc/programming_rules.shtml#REF28631

Your message processing order is deterministic - you always process ^token2 before ^token1. It is the message arrival order that is not deterministic because your messages come from two different processes - but that is irrelevant in this case because you are forcing the order of processing.

Try this instead:

receive do
  ^token1 -> IO.puts "Got reply: #{token1}"
  ^token2 -> IO.puts "Got reply: #{token2}"
end
receive do
  ^token1 -> IO.puts "Got reply: #{token1}"
  ^token2 -> IO.puts "Got reply: #{token2}"
end

… and even then execution may still favour one order heavily over any other.

3 Likes

Exactly this yes. :slight_smile:

Once again, awesome answers guys. Thanks for helping a newbie out.