I don’t see this as violating the purpose of monitors. Both the message signal and the 'DOWN'
signal are signals with payloads that can be converted into messages that you can match on in a receive
expression. In both cases you need to tag the data, so that you can distinguish between different scenarios. The exit reason is also there for propagating a value. What kind of value to propagate depends on what you do.
In general I’d refrain from using links for this, though, even though links and 'EXIT'
signals are very similar to monitors and 'DOWN'
signals. This since a link can be operated on in both ends, and you may need to modify the trap_exit
state on the “client” which will alter how it reacts to other incoming exit signals. However, in some special case it might be useful to use a link, though.
The, in OTP 23 introduced, erpc
module is based on the monitor approach. In the erpc:call()
case we know very little about the code that is going to execute, but in order to distinguish between different scenarios, like the process was terminated by an exit signal, the process was terminated due to an uncaught exception, and an ordinary return value, the only thing you need to do is to tag the exit reason with a termination type (return/throw/exit/error) and a reference created by the “client”.
The monitor approach has also been used in OTP itself for much longer than that. One of the reasons spawn_monitor()
was introduced was due to a bug that we found when timing changed at the introduction of the SMP runtime system. The code essentially did:
Pid = erlang:spawn(fun () -> exit(do_something_and_produce_a_result()) end),
Mon = erlang:monitor(process, Pid),
receive
{'DOWN', Mon, process, Pid, Result} ->
Result
end
Due to the changed timing, the process identified by Pid
every now and then had terminated prior to the monitor being set up which resulted in the result noproc
instead of the actual result. This could happen in the non-SMP runtime as well, but we had never seen it since you needed to run out of reductions exactly after the call to spawn()
for it to happen. It can be noted that this was actually one of very few bugs in the Erlang code of OTP that was exposed by the changed timing of the SMP runtime system. We had expected more issues to appear.
The spawn_request()
BIF was partly introduced in order to make it possible to do things like this efficiently also in the distributed case (both the asynchronous part of it and the monitor part of it). It is a building block of erpc
since implementing it with the synchronous spawn BIFs would make multicall
very inefficient and timeouts would never be able to trigger prior to the response from the synchronous spawn. It is, however, not the only purpose of spawn_request()
, it is for use in any scenario where you want to spawn processes, and is a much better spawn primitive than the other old synchronous spawn BIFs. This since you may need to spawn asynchronously also in other scenarios when you need to spawn processes. You can also implement the old spawn primitives using spawn_request()
, but you cannot do it the other way around, at least not without ending up with a very inefficent spawn_request()
. This is also more or less how it is implemented in the distributed case, there is only one distributed asynchronous spawn primitive which all distributed spawn operations are based on.