Given two linked processes child and parent, how does process child detect that parent exits (terminates) normally?
I, as an absolute Erlang beginner, thought that a process, when it has nothing else to do, exited using exit(normal). This then signals all linked processes, where
- the behaviour of processes that have
trap_exitset tofalseis to ignore the signal, and - the behaviour of processes that have
trap_exitset totrueis to generate the message{'EXIT', pid, normal}wherepidis the process id of the terminating process.
My reason for thinking this is Learn You Some Erlang for Great Good and the Erlang documentation which states the following.
A process is said to terminate normally, if the exit reason is the atom normal. A process with no more code to execute terminates normally.
Apparently that is wrong (?), because exit(normal) shows ** exception exit: normal in the command prompt and makes the code below work. Exiting because there is no more code to execute does not generate the exception and does not make my code work.
As an example, consider the following code.
-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 produces output 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 had to manually execute the exit(pid(0,176,0),something) command to keep the child from staying alive forever.
Changing ok. in start to exit(normal) makes the execution go like this
(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 concrete questions are the following.
- How can I make the above code work as expected. That is, how can I make sure the child process dies together with the parent process without changing the parent process?
- Why does
exit(normal)generate a** exception exit: normalin the CLI? It is hard for me to think of an exception as something that is normal. What does the scentence in the Erlang documentation mean?
I think these must be extremely basic questions, but I can’t seem to figure this out….
I am using Erlang 5.9.3.1 on Windows (x64).
Erlang shell has the worker for evaluating commands as a separate process, and all commands you type run by the same process. When you your
startfunction finished, worker still alive, and when you kill it by exit(), shell understand as worker exception (because worker will never die in normal case).So:
spawnorspawn_linkP.S. sorry for my english
P.P.S.
spawn(fun() -> test:start() end).works as expected