I agree with the motivation and the see the relevance of the problematic scenarios listed in the EEP. But, to be honest, I’m not entirely sold to the proposed solution.
- One problem I have is that the receiver needs to opt-in for priority messages per sender. The current practice is that only the sender needs to know who will be the receiver of a message, a process generally doesn’t care about who will send a message to it. Perhaps in these specific problematic scenarios the receiver would know who may send a priority message to it in the future, but there are many scenarios when the sender is not known. For example it may be useful to schedule a priority message with
erlang:send_after/4
, but you don’t know what process to expect that message from.
- The EEP deals with regular messages from a process, exit signals and monitor signals. They all need different
process_flag/2
-s for opting in, which is already a lot of complexity. monitor/3
gets a new option to make this easier to use, but for a complete priority-message support you’d need similar extensions to a lot of other API-s that set up some message to be sent in the future: timers, erlang:send_after/4
, erlang:monitor_node/3
, net_kernel:monitor_nodes/2
, trace messages, OS signal handling, active messages sent by gen_tcp
, the {tcp_passive, Socket}
message sent upon exhausting the receive window of an {active, N}
socket and so on. A complete support of priority message (where any message you may expect from Erlang/OTP can be configured to be sent as priority) would be a huge change, with a lot of updates both in the client API-s and in the implementation of the services (so they know whether to send regular or priority messages).
- A minor note is that I don’t see the EEP mentioning NIF-s. NIF-s would also need an API for sending priority messages.
If I may propose an alternative solution to consider, I think indexed message queues could solve this problem with less complexity (at least in the language and library level). The idea is that a process could specify one or more patterns (in the form of a match specification) used for indexing its message queue. This could be a process_flag/2
, for example like this:
process_flag(
message_queue_indexing,
[ { {'EXIT', '_', '_'}, [], [] }, % index EXIT messages
{ {immediate_abort, [], [] } % index some arbitrary message relevant for this process
])
This call would create 2 indices for the process.
When a message is placed in the message queue of a process with message_queue_indexing
, the message is tested against the match spec, and gets inserted into all of the indices too (an index wouldn’t be a separate message queue, just something like a linked list of pointers to the actual messages in the one and only message queue).
Now, the trick is that when the process later does a selective receive, the receive patterns could be compared with the match spec and if all of the receive patterns are indexing patterns too, then the receive would only have to scan those indices, not the entire message queue.
This would help speeding up code written in the following (I believe quite common) style, without messing with message ordering guarantees in any way:
receive
immediate_abort ->
exit(shutdown);
{'EXIT', Pid, Reason} when Pid =:= MyWorker ->
restart_worker()
after
0 ->
receive
Msg ->
handle_msg(Msg)
end
end
The downside is that it’s obviously a harder problem to implement these indexed receives in the compiler and ERTS (e.g. the compiler would have to turn the patterns to match specifications and then ERTS would have to figure out whether the index match specification fully covers the receive match specification; not to mention maintaining the indices as messages are inserted and removed from the message queue).