Running statements in shell gives different output - erlang

Simple code:
-module(on_exit).
-export([on_exit/2, test/0]).
on_exit(Pid, Fun) ->
spawn(fun() ->
Ref = erlang:monitor(process, Pid),
receive
{'DOWN', Ref, process, Pid, Why} ->
Fun(Why)
end
end).
test() ->
Fun1 = fun() -> receive Msg -> list_to_atom(Msg) end end,
Pid1 = spawn(Fun1),
Fun2 = fun(Why) -> io:format("~w died with error: ~w~n", [Pid1, Why]) end,
_Pid2 = spawn(on_exit, on_exit, [Pid1, Fun2]),
Pid1 ! hello.
In the shell:
1> c(on_exit).
{ok,on_exit}
2> on_exit:test().
<0.39.0> died with error: noproc
hello
3>
=ERROR REPORT==== 9-Apr-2017::05:16:54 ===
Error in process <0.39.0> with exit value: {badarg,[{erlang,list_to_atom,[hello],[]},{on_exit,'-test/0-fun-0-',0,[{file,"on_exit.erl"},{line,14}]}]}
Expected Output:
5> Pid1 ! hello.
<0.35.0> died with error: {badarg,[{erlang,list_to_atom,[hello],[]}]}
hello
6>
=ERROR REPORT==== 9-Apr-2017::05:15:47 ===
Error in process <0.35.0> with exit value: {badarg,[{erlang,list_to_atom,[hello],[]}]}
In fact, the expected output is what I see if I take each line in test() and paste it into the shell. Why do I get the noproc (no process) error when I run the same lines inside a function?
From the docs:
12.8 Monitors
An alternative to links are monitors. A process Pid1 can create a
monitor for Pid2 by calling the BIF erlang:monitor(process, Pid2). The
function returns a reference Ref.
If Pid2 terminates with exit reason Reason, a 'DOWN' message is sent
to Pid1:
{'DOWN', Ref, process, Pid2, Reason}
If Pid2 does not exist, the 'DOWN' message is sent immediately with
Reason set to noproc.

Your code contains a race condition -- spawn is asynchronous and might return before the process is spawned, and you might end up sending and crashing Pid1 before on_exit:on_exit/2 starts monitoring it, which causes the erlang:monitor/2 call to immediately send a noproc message to the caller:
1> Pid = spawn(fun() -> ok end).
<0.59.0>
2> erlang:monitor(process, Pid).
#Ref<0.0.1.106>
3> flush().
Shell got {'DOWN',#Ref<0.0.1.106>,process,<0.59.0>,noproc}
ok
The code works fine in the shell probably because the Erlang VM executes some things slowly in the shell than when the code is compiled, but this behavior is not guaranteed. This is a classic race condition.
Erlang has a solution for this: erlang:spawn_monitor/{1,3}. This function is guaranteed to attach the monitor as soon as the function is spawned. You'll have to re-arrange your code a bit to use it instead of spawn/3 + erlang:monitor/1.

Related

Why doesn't spawn link cause the calling process to die?

From the example given here, Erlang and process_flag(trap_exit, true)
-module(play).
-compile(export_all).
start() ->
process_flag(trap_exit, true),
spawn_link(?MODULE, inverse, [***0***]),
loop().
loop() ->
receive
Msg -> io:format("~p~n", [Msg])
end,
loop().
inverse(N) -> 1/N.
If I run it as,
A = spawn(play, start, []).
The spawned process <0.40.0> dies as it is suppose to but the main process (A <0.39.0>) which spawned it doesn't die.
{'EXIT',<0.40.0>,{badarith,[{play,inverse,1,[{file,"play.erl"},{line,15}]}]}}
<0.39.0>
i().
....
....
<0.39.0> play:start/0 233 19 0
play:loop/0 1
A does get an exit signal (not an exit message since A is not trapping exit) then why doesn't it exit?
The reason for this is that you set a trap_exit flag to true which means this process will get {'EXIT', FromPid, Reason} message instead of being killed. Just remove process_flag(trap_exit, true) or in case of receiving this type of message, kill it.
You can read about it here.

Avoiding a race condition in erlang

Is this how you avoid a race condition?
-module(b).
-export([my_spawn/1]).
my_spawn(Func) ->
Pid = spawn(listener()),
Pid ! {self(), spawn, Func},
receive
{From, Desired_Pid} -> Desired_Pid
end.
listener() ->
receive
{From, spawn, Func} ->
{Pid,Ref} = spawn_monitor(Func),
From ! {self(), Pid},
receive
{'DOWN',Ref, process, _, _} -> io:format("I lived for [calculated how long i lived]")
end
end.
What I'm trying to achieve here is
A = spawn(proc),
monitor(process,A).
However A might die before the second line executes.
If you spawn a process and it dies just before you create your monitor, you will still receive the DOWN message:
1> Pid = spawn(erlang,now,[]).
<0.35.0>
2> is_process_alive(Pid).
false
3> monitor(process, Pid).
#Ref<0.0.0.86>
4> flush().
Shell got {'DOWN',#Ref<0.0.0.86>,process,<0.35.0>,noproc}
As this shell session shows, we first spawn an intentionally short-lived process, and we use is_process_alive to verify that it's dead. We then create a monitor for the process, and then we flush the shell's message queue and see that it did indeed receive a DOWN message for the already-deceased process.

Erlang spawn simple process from erl .. no such process or port

When running this code in the Erlang console
Pid = spawn(fun() -> "foo" end),link(Pid),receive X -> X end.
I receive the following error.
** exception error: no such process or port
in function link/1
called as link(<0.71.0>)```
This happens because the process you spawn finishes very quickly: it only "returns" a string (and the return value goes nowhere, since it is the top-level function in the call stack of the new process), so it's very likely to finish before the emulator gets to the link call.
You can make it more likely to succeed by making the process sleep before exiting:
2> Pid = spawn(fun() -> timer:sleep(1000), "foo" end),link(Pid).
true
Note however that the receive expression in your example most likely won't receive anything, since the spawned process doesn't send any message, and the link won't generate any message either since the process exits normally, and the calling process most likely isn't trapping exits. You may want to do something like:
Parent = self(),
spawn(fun() -> Parent ! "foo" end),
receive X -> X end.
That returns "foo".

Erlang pids from console

I saw message:
https://stackoverflow.com/a/4837832/1236509
with supervisor:
-module(root_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
{ok, Pid} = supervisor:start_link({local, ?MODULE},
?MODULE, []),
erlang:unlink(Pid),
{ok, Pid}.
init(_Args) ->
RestartStrategy = {simple_one_for_one, 10, 60},
ChildSpec = {ch1, {ch1, start_link, []},
permanent, brutal_kill, worker, [ch1]},
Children = [ChildSpec],
{ok, {RestartStrategy, Children}}.
In console man calls:
{ok, ChildPid1} = root_sup:start_link().
when child pid changes how would ChildPid1 get new pid so always can use ChildPid1 with correct pid? Need way to link to part of supervisor creating child.
I would not try to access the child by Pid but instead register/2 the child process under a name, so it's accessible regardless of the actual Pid.
Using the code from the answer you reference, a simple way of doing this is to add register(ch1, self()), to the init procedure of the child. This would give, for ch1.erl:
init(_Args) ->
io:format("ch1 has started (~w)~n", [self()]),
% register a name to this process
register(child, self()),
{ok, ch1State}.
This registers the pid of the child self() to the name child
We can see it works:
1> root_sup:start_link().
{ok,<0.34.0>}
2> supervisor:start_child(root_sup, []).
ch1 has started (<0.36.0>)
{ok,<0.36.0>}
3> lists:filter(fun(X) -> X == child end, registered()).
[child]
we indeed have a process registered under the name of child.
4> gen_server:cast(child, calc).
result 2+2=4
and it is a correct process running the code from ch1.erl.
Let us crash this process, by invoking the bad code:
5> gen_server:cast(child, calcbad).
result 1/0
ok
ch1 has started (<0.41.0>)
6>
=ERROR REPORT==== 28-Oct-2012::01:31:30 ===
** Generic server <0.36.0> terminating
** Last message in was {'$gen_cast',calcbad}
** When Server state == ch1State
** Reason for termination ==
** {'function not exported',
[{ch1,terminate,
[{badarith,
[{ch1,handle_cast,2,[{file,"ch1.erl"},{line,27}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,607}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
ch1State],
[]},
{gen_server,terminate,6,[{file,"gen_server.erl"},{line,722}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,227}]}]}
So the child, the process <0.36.0> crashed, and a new child <0.41.0> was started to assume the duties of the deceased <0.36.0>. Since this new process is registered under the same name, calc will work again:
6> gen_server:cast(child, calc).
result 2+2=4
ok
Note that this does not guarantee that the gen_server:cast/2s always result in the execution of the corresponding code because the child might have been killed just now and the new process still was not started (registered in fact).
You might want to refer to the excellent Programming Erlang: Software for a Concurrent World by Joe Armstrong for more details about process registration, supervisors, OTP and more. Many details can also be found in the online documentation of OTP.

spawn_link not working?

I'm using spawn_link but doesn't understand its behavior. Consider the following code:
-module(test).
-export([try_spawn_link/0]).
try_spawn_link() ->
spawn(fun() ->
io:format("parent: ~p~n", [Parent = self()]),
Client = spawn_link(fun() ->
io:format("child: ~p~n", [self()]),
spawn_link_loop(Parent)
end),
spawn_link_loop(Client)
end).
spawn_link_loop(Peer) ->
receive
quit ->
exit(normal);
Any ->
io:format("~p receives ~p~n", [self(), Any])
end,
spawn_link_loop(Peer).
From the Erlang documentation, a link is created between the calling process and the new process, atomically. However, I tested as follows and didn't notice the effect of the link.
1> test:try_spawn_link().
parent: <0.34.0>
<0.34.0>
child: <0.35.0>
2> is_process_alive(pid(0,34,0)).
true
3> is_process_alive(pid(0,35,0)).
true
4> pid(0,35,0) ! quit.
quit
5> is_process_alive(pid(0,35,0)).
false
6> is_process_alive(pid(0,34,0)).
true
1> test:try_spawn_link().
parent: <0.34.0>
<0.34.0>
child: <0.35.0>
2> is_process_alive(pid(0,34,0)).
true
3> is_process_alive(pid(0,35,0)).
true
4> pid(0,34,0) ! quit.
quit
5> is_process_alive(pid(0,35,0)).
true
6> is_process_alive(pid(0,34,0)).
false
In my understanding, if one peer of the link exits, the other peer exits (or is notified to exit). But the results seem different from my understanding.
EDIT: thanks to the answers of legoscia and Pascal.
It is because you have chosen to use exit(normal). In this case the other process will not stop. If you use for example exit(killed) then you will get the behavior you are expecting.
You can use monitor to get informed about normal termination.
As described in the "Error handling" section of the Processes chapter of the Erlang reference manual, a linked process exiting causes its linked processes to exit only if the exit reason is not normal. That's why OTP extensively uses the shutdown exit reason.

Resources