I am learning Erlang and I am confused by the following code:
-module(killing_master).
-export([start/1, loop/1]).
start(SlavesNum) ->
process_flag(trap_exit, true),
[spawn_link(fun() ->
io:format("slave ~p~n", [N]),
loop(N)
end) || N <- lists:seq(1, SlavesNum)],
spawn_link(fun() -> process_flag(trap_exit, true), init() end),
exit(normal).
init() ->
receive
{'EXIT', From, Why} -> io:format("start: (~p) exited ~p ~p~n", [self(), From, Why]);
Catch -> io:format("~p~n", [Catch])
end.
loop(SlaveNum) ->
receive
Msg -> io:format("slave #~p received ~p~n", [SlaveNum, Msg])
end.
The behaviour I am expecting is that after the master exits all slaves should exit too (because of spawn_link
), printing a message before doing so; insteand I am getting this:
1> killing_master:start(10).
slave 1
slave 2
slave 3
slave 4
slave 5
slave 6
slave 7
slave 8
slave 9
slave 10
** exception exit: normal
The behaviour I am expecting is that after the master exits all slaves should exit too (because of spawn_link), printing a message before doing so
In addition, none of the slave processes are trapping exits, so they won't print a message when they receive an exit signal. Your slaves are created inside a list comprehension here:
[spawn_link(fun() ->
io:format("slave ~p~n", [N]),
loop(N)
end)
|| N <- lists:seq(1, SlavesNum)],
And, each slave process runs the function:
fun() ->
io:format("slave ~p~n", [N]),
loop(N)
end
and the loop/1
function doesn't trap exits either.
Here's some simpler code you can try:
-module(a).
-compile(export_all).
master() ->
process_flag(trap_exit, true),
io:format("master pid: ~w~n", [self()]),
spawn_link(fun slave/0),
receive
{'EXIT', From, Why} ->
io:format("master(): Process (~w) exited~nExit signal from: ~w~nReason: ~w~n",
[self(), From, Why]);
Any ->
io:format("master(): non-exit message... ~w~n", [Any])
end.
slave() ->
process_flag(trap_exit, true),
io:format("slave 1~n"),
receive
{'EXIT', From, Why} ->
io:format("slave(): Process (~w) exited~nExit signal from: ~w~nReason: ~w~n",
[self(), From, Why]);
Any ->
io:format("slave(): non-exit message... ~w~n", [Any])
end.
In the shell:
~/erlang_programs% erl
Erlang/OTP 24 [erts-12.3.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1]
Eshell V12.3.2 (abort with ^G)
1> c(a).
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
% 2| -compile(export_all).
% | ^
{ok,a}
2> Master = spawn(a, master, []).
master pid: <0.90.0>
<0.90.0>
slave 1
3> exit(Master, normal).
master(): Process (<0.90.0>) exited
Exit signal from: <0.83.0>
Reason: normal
true
slave(): Process (<0.91.0>) exited
Exit signal from: <0.90.0>
Reason: normal
4> self().
<0.83.0>
The reason could be that the master dies even before the spawning of the slave child.
In that case, I would think that spawn_link
would return something like {nolink, new_pid}
. It seems strange that there is no way to know if the link was successful. You can confirm that the master process has in fact exited with this code:
-module(a).
-compile(export_all).
master() ->
io:format("master pid: ~w~n", [self()]),
spawn_link(fun slave/0).
slave() ->
process_flag(trap_exit, true),
io:format("slave 1~n"),
receive
{'EXIT', From, Why} ->
io:format("slave(): Process (~w) exited~nExit signal from: ~w~nReason: ~w~n",
[self(), From, Why]);
Any ->
io:format("slave(): non-exit message... ~w~n", [Any])
end.
In the shell:
> c(a).
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
% 2| -compile(export_all).
% | ^
{ok,a}
2> Master = spawn(a, master, []).
master pid: <0.90.0>
<0.90.0>
slave 1
3> i().
...
...
disk_log_server gen_server:loop/7 12
<0.80.0> disk_log:init/2 4185 12507 0
disk_log:loop/1 7
<0.82.0> erlang:apply/2 28690 19928 0
shell:shell_rep/4 18
<0.83.0> erlang:apply/2 121536 46746 0
c:pinfo/1 49
<0.91.0> erlang:apply/2 233 33 0
a:slave/0 2
Total 213936 561931 0
The master()
process pid was <0.90.0>
, which isn't listed, and you can see that slave()
is running in process <0.91.0>
. Or, you can call:
4> is_process_alive(Master).
false
Yet, slave()
did not output any exit messages. The conclusion is that the master process exited before the slave process was ever linked to it.
Therefore, your code has two problems: