Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the purpose to introduce 'save queue' in erlang receive statement

Tags:

erlang

I am new to erlang and starting tutorial from 'Programming Erlang' by Joe Armstrong.

I feel puzzled about the 'save queue' mentioned in the selective receive in 8.6. If the message is not matched at all, why not drop it directly? What's the purpose of putting it back to the mailbox for later processing? If it's default behavior, those garbage messages (meaning that they cannot be matched) could lead to performance penalties as they will accumulate without release.

I would like to know whether I misunderstood this part. I tried to inspect the contents of a process mailbox to get a better understanding but I was unable to.

Please help, I would appreciate any code snippet that proves a point, thanks.

like image 283
Rex Avatar asked Dec 07 '22 09:12

Rex


2 Answers

This paragraph of the book describes the details of what is done by one receive bloc. Don't forget that it is possible to have several receive blocs processed sequentially by one single process, so one message which doesn't match any entry of the first receive bloc will be put in the save queue (for efficiency) because:

  • it will never match any entry of the current receive bloc,
  • the current receive bloc is guaranteed to finish only when one entering message matches one entry or the timeout ends.

When one receive bloc is done, then the save queue is put back, in original receive order, in the mail box because the next receive bloc has a chance to have an entry which match one of the "save queued" messages.

One usage is to manage priorities:

loop() ->
   ...
   receive
      {high_prio,Msg} -> process_priority_message(Msg)
   after 0
      ok  % no priority message
   end,

   receive
      {low_prio,Msg} -> process_normal_message(Msg);
      Other -> process_unexpected_message(Other)
   end,
   ...
   loop()

This code allow to process a message {high_prio,Msg} even if it is not in the first position within the message queue.

And you are right, there is a risk that unexpected messages accumulate in the mail box, especially in a never ending loop, it is why you will see very often something like the last line

Other -> process_unexpected_message(Other)

to empty the mail box.

like image 118
Pascal Avatar answered May 30 '23 13:05

Pascal


It is an enormous help when building concurrent systems as it allows you to only concentrate on those messages in which you are interested at that point and ignore other messages. Erlang systems are typically non-deterministic so you seldom know what you are going to receive and when. Without the automatic save queue it would mean that at every point you receive a message you would have to be able to handle every message that could possibly arrive at this process. It very quickly becomes a combinatorial explosion of states and messages.

As an example take simple server. At its top-level there will be a receive loop which receives requests for the server to handle. It will then process the first request. Most likely during this processing it will be communicating with other processes, and receiving messages. While processing a request a new request message can arrive at the server. If Erlang didn't save the messages then you would have to handle these requests everywhere in the server code where messages are received. Now you can ignore these new requests and leave them for the top-loop which should handle them.

Keeping track of all messages that need to be handled somewhere in your server quickly becomes unworkable. For example in a gen_server you have the actual requests sent by clients (message format unspecified), "system" messages to the server (message format unspecified), any number of well-defined messages that are meaningful to the server code in addition to all the messages your processing of requests needs.

You would end up implementing your own message buffer and passing it around.

Not having the message save queue would make it practically impossible to write generic modules which send/receive messages while processing, for example the client functions to gen_servers. They would have to know about every message that could arrive at that process and needs to be handled.

Yes, saving all messages can be a problem, but it is usually the type of problem which is solvable when you are aware of it. For example in the top-loop of a server you can be reasonably certain that an unknown message can be received and thrown away. A gen_server does receive all messages at it top-level where it knows what to do with them, some it processes itself (the system messages) and the others it passes on the specific server code.

And it allows you to easily handle priority messages as @Pascal showed.

like image 45
rvirding Avatar answered May 30 '23 13:05

rvirding