Processes exiting normally
Given the two related processes child
and parent
how does the process child
find out that it parent
exits (exits) normally?
I, as an absolute beginner Erlang, believed that the process, when it had nothing else to do, exited with exit(normal)
. This then signals all related processes where
- the behavior of processes
trap_exit
set tofalse
this is to ignore the signal, and - the behavior of processes
trap_exit
set to this valuetrue
should generate a message{'EXIT', pid, normal}
, wherepid
is the process ID of the terminating process.
My reason for thinking this is Find out you have Erlang for Great Good and Erlang Documentation stating the following.
It is said that the process ends normally if the reason for the exit is a normal atom. A process with no code to run usually ends.
Apparently this is not correct (?) Because exit(normal
) shows ** exception exit: normal
on the command line and does the code below. Exiting for lack of code to execute does not throw an exception or make my code work.
Consider the following code as an example.
-module(test). -export([start/0,test/0]). start() -> io:format("Parent (~p): started!\n",[self()]), P = spawn_link(?MODULE,test,[]), io:format( "Parent (~p): child ~p spawned. Waiting for 5 seconds\n",[self(),P]), timer:sleep(5000), io:format("Parent (~p): dies out of boredom\n",[self()]), ok. test() -> io:format("Child (~p): I'm... alive!\n",[self()]), process_flag(trap_exit, true), loop(). loop() -> receive Q = {'EXIT',_,_} -> io:format("Child process died together with parent (~p)\n",[Q]); Q -> io:format("Something else happened... (~p)\n",[Q]) after 2000 -> io:format("Child (~p): still alive...\n", [self()]), loop() end.
This outputs the result as follows.
(erlide@127.0.0.1)> test:start().
Parent (<0.145.0>): started!
Parent (<0.145.0>): child <0.176.0> spawned. Waiting for 5 seconds
Child (<0.176.0>): I'm... alive!
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Parent (<0.145.0>): dies out of boredom
ok
(erlide@127.0.0.1)10> Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
Child (<0.176.0>): still alive...
exit(pid(0,176,0),something).
Child process died together with parent ({'EXIT',<0.194.0>,something})
If you had to manually execute a command exit(pid(0,176,0),something)
to keep the child forever. Changing ok.
in start
to exit(normal)
makes the execution the same as
(erlide@127.0.0.1)3> test:start().
Parent (<0.88.0>): started!
Parent (<0.88.0>): child <0.114.0> spawned. Waiting for 5 seconds
Child (<0.114.0>): I'm... alive!
Child (<0.114.0>): still alive...
Child (<0.114.0>): still alive...
Parent (<0.88.0>): dies out of boredom
Child process died together with parent ({'EXIT',<0.88.0>,normal})
** exception exit: normal
My specific questions are as follows.
- How can I get the above code to work as expected. That is, how can I ensure that the child process dies along with the parent process without changing the parent process?
- Why does it
exit(normal)
generate** exception exit: normal
in the CLI? I find it hard to think of an exception as normal. What does smell mean in Erlang documentation?
I think these should be extremely simple questions, but I cannot figure it out. I am using Erlang 5.9.3.1 for Windows (x64).
Erlang shell has a worker for evaluating commands as a separate process, and all commands you enter are run by the same process. When you finish your function start
, the worker is still alive, and when you kill it with exit (), the shell understands it as a worker exception (because the worker will never die normally).
So:
- You must run as a separate process with
spawn
orspawn_link
- The CLI registers all work outputs as exceptional and normal.
Ps sorry for my english
PPS spawn(fun() -> test:start() end).
works as expected
4> spawn(fun() -> test:start() end).
Parent (<0.41.0>): started!
<0.41.0>
Parent (<0.41.0>): child <0.42.0> spawned. Waiting for 5 seconds
Child (<0.42.0>): I'm... alive!
Child (<0.42.0>): still alive...
Child (<0.42.0>): still alive...
Parent (<0.41.0>): dies out of boredom
Child process died together with parent ({'EXIT',<0.41.0>,normal})
Comment on your question at @PetrKozorezov's answer. The shell doesn't behave on purpose. The shell worker process is a normal process, so if any process it is associated with crashes, then it will crash as well. Then another workflow will be launched. This is the usual Erlang way.
Function start/0
just returns and does NOT terminate its process, it just prints the message "to die of boredom". This is why the cycle continues to move, it does not receive a signal exit
, because no process has died.
When you change the function start/0
to end with exit(normal)
, then , you end the shell process, so the signal is exit
sent to the loop, which then receives the {'EXIT',...,...}
message and stamps.
When @PetrKozorezov spawned your original function start/0
in a separate process, which then died after being executed start/0
, it sent an exit signal normal
to the loop process which caused it to die.
This is perfectly normal Erlang behavior and style. Typically, you don't terminate the launch function with a help exit
, but leave it to the caller to decide when to die.
Another small point: since the start function does spawn_link
, you usually call it start_link
. Function A start
is considered a process only spawn
. This is of course just a convention, but a general one, so you haven't made a mistake.