If you execute Process.exit(pid, :normal), for a monitored process, the :DOWN message is not received. For example,
child = spawn(fn -> Process.sleep(:infinity) end)
Process.monitor(child)
Process.exit(child, :normal)
receive do
{:DOWN, _ref, :process, _monitored_pid, reason} ->
IO.puts("Monitored process down with reason #{reason}")
after
5000 ->
IO.puts("Timeout")
end
prints “Timeout”.
Why is that? Do you see it documented anywhere in Elixir or Erlang?
Interestingly, Process.exit(pid, :kill) does trigger the :DOWN message, though.
Exiting a process with a reason of :normal flags that the termination is normal (for example the process is done processing and everything went OK). Because this is not considered an error state the :DOWN message is not sent. Exiting a process with any other exit code generates the exit message.
:normal and :kill are are special to exit. Normal means everything went OK and kill is not trappable meaning the process will die and you can’t do anything about it. Any other reason is for informational purposes and can be trapped.
This behaviour is documented (although perhaps not the why) in Process.exit in elixir and exit/2 in erlang.
BTW, note that a :normal exit reason when the process terminates (for example, by having no more code to execute) does send :DOWN messages. Monitors monitor when processes terminate with any reason, they are not only monitoring abnormal termination.
The real problem in my script is that the process does not terminate.
Exactly, Process.exit(pid, :normal) is silently ignored if pid is different from self() (the caller pid). Note that it still terminates the process if called like Process.exit(self(), :normal), or if called with any pid and a reason different than :normal.