程序代写代做代考 C interpreter chain Erlang Message Passing

Message Passing
CS511
1/61

Message Passing
Exceptions
Links and Monitors
2/61

Interaction Models
􏰟 Previously
􏰟 Shared memory (low-level, non-structured)
􏰟 Semaphores (low-level, non-structured)
􏰟 Monitors (popular, structured, encapsulate synchronization)
􏰟 So what’s the problem with monitors?
􏰟 Highly centralized (un/blocking processes, maintaining queues
of blocked processes, encapsulating data)
􏰟 For modern, distributed architectures, need for less centralized
solution
􏰟 Turn to interaction through communication rather than sharing
3/61

The Message Passing Model
􏰟 No shared memory
􏰟 A process sends a message
􏰟 Another process receives the message
􏰟 Operations: receive(Var);
send(PID,msg);
􏰟 receive blocks until a message is available in the mailbox
􏰟 send(PID,msg) is non-blocking; it sends message msg to process
PID
􏰟 This model is the asynchronous communication model and is the one used in Erlang
4/61

Nodes and Processes in Erlang1
􏰟 A distributed Erlang system consists of a number of Erlang runtime systems communicating with each other (instances of the VM)
􏰟 Each such runtime system is called a node
1Source: https://blog.stenmans.org/theBeamBook/
5/61

Distributed Nodes in Erlang
􏰟 A distributed Erlang system consists of a number of Erlang runtime systems communicating with each other (instances of the VM)
6/61

Nodes and Processes in Erlang
􏰟 Each such runtime system is called a node 􏰟 node name is an atom name@host
􏰟 name is the name given by the user
􏰟 host is the full host name if long names are used, or the first
part of the host name if short names are used
􏰟 The name of a node may be consulted using node()
1 1> node().
2 nonode@nohost
7/61

Processes and Communication in Erlang
􏰟 A process in a node has 􏰟 a process id (pid)
1 1> self().
2 <0.78.0 >
􏰟 its own memory (a mailbox, a heap and a stack); and
􏰟 a process control block (PCB) with information about the
process.
􏰟 Message passing between processes at different nodes, as well as links and monitors, are transparent when pids are used
􏰟 Registered names, however, are local to each node.
􏰟 Format of a PID:
􏰟 node id where process lives; 0 if node is local
􏰟 process index itelf (index into process table)
􏰟 serial which increases every time MAXPROCS has been
reached.
8/61

A Simple Echo Server
1 2 3 4 5 6 7
􏰟 Process echo will receive a message and then send it back to the sender
􏰟 After that it will continue to wait for a new message
􏰟 It may be stopped by sending it the stop message
echo() -> receive
{From, Msg} -> From ! {Msg},
echo (); stop -> true
end.
􏰟 Processes are created using spawn/1 and spawn/3
9/61

A Simple Echo Server (cont.)
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20 21 22
-module(echo). -export([start/0]).
echo() -> receive
{From, Msg} -> From ! {Msg},
echo (); stop -> true
end.
start() ->
Pid = spawn(fun echo/0), % Returns pid of a new process
% started by the application of echo/0 to []
Token = “Hello Server!”, % Sending tokens to the server Pid ! {self(), Token},
io:format(“Sent ~s~n”,[Token]),
receive
{Msg} ->
io:format(“Received ~s~n”, [Msg])
end,
Pid ! stop. % Stop server
10/61

A Simple Echo Server
1 2 3 4
1 2 3 4 5 6
1> echo:start( ).
Sent Hello Server! Received Hello Server! stop
If we export echo/0 we can spawn from the interpreter:
59> X=spawn(fun echo:echo/0). <0.198.0 >
60> X!{self(),”hello”}. {<0.60.0>,”hello”}
61> X. <0.198.0 >
Note: the value of a send is the value of the message
11/61

Reacting to Multiple Messages
􏰟 Erlang “listens” for messages from different senders
􏰟 In which order will they be processed?
􏰟 Can we force an order?
􏰟 A receive statement tries to find a match as early in the
mailbox as it can
1 receive
2 msg3 -> 42 3 end
12/61
msg1
msg2
msg3
msg4
mailbox
msg1
msg2
msg4
mailbox

Reacting to Multiple Messages
1 receive
2 msg4 -> 42 3 end
msg1
msg2
msg4
mailbox
msg1
msg2
mailbox
13/61

Reacting to Multiple Messages
Waiting for multiple messages
1 receive
2 msg4 -> 42; 3 _ ->41 4 end
msg1
msg2
msg4
mailbox
msg2
msg4
mailbox
􏰟 The oldest message is tried against every pattern of the receive until one of them matches
14/61

Sources of Multiple Messages
Multiple messages can come from different processes
Process B
Process A
Process C
􏰟 How do we know who sent a message? 􏰟 Distinguish the source by Pids
15/61

Sources of Multiple Messages
1 2 3 4 5 6 7 8 9
10 11 12 13 14
-module(echo2). -export([start/0]).
echo() -> receive
{From, Msg} -> timer:sleep(rand:uniform(100)), From ! {Msg},
echo ();
%
stop -> true
end.
continued on next
slide …
􏰟 timer:sleep(N) sleeps a process for N milliseconds
􏰟 rand:uniform(N) produces a random integer between 1 and N
16/61

Sources of Multiple Messages
1 start() ->
2 PidB = spawn(fun echo/0),
3 PidC = spawn(fun echo/0),
4
5 % sending tokens
6 Token = 42,
7 PidB ! {self(), Token},
8 io:format(“Sent~w~n”,[Token]),
9 Token2 = 41,
10 PidC ! {self(), Token2},
11 io:format(“Sent~w~n”,[Token2]),
12
13 % receive message
14 receive
15 {Msg} ->
16 io:format(“Received ~w~n”, [Msg])
17 end,
18
19 % stop echo-servers
20 PidB ! stop ,
21 PidC ! stop.
17/61

Sources of Multiple Messages
1 2 3 4 5 6 7 8 9
10 11 12 13 14
􏰟 How do we know who sent a message?
􏰟 Distinguish the source by Pids -module(echo2).
-export([start/0]).
echo() -> receive
%
stop -> true
end.
continued on next
slide …
{From, Msg} -> timer:sleep(rand:uniform(100)), From ! {self(), Msg},
echo ();
18/61

Sources of Multiple Messages
1 start() ->
2 PidB = spawn(fun echo/0),
3 PidC = spawn(fun echo/0),
4
5 % sending tokens
6 Token = 42,
7 PidB ! {self(), Token},
8 io:format(“Sent~w~n”,[Token]),
9 Token2 = 41,
10 PidC ! {self(), Token2},
11 io:format(“Sent~w~n”,[Token2]),
12
13 % receive messages
14 receive
15 {PidB, Msg} ->
16 io:format(“Received from B: ~w~n”, [Msg]) ;
17 {PidC, Msg} ->
18 io:format(“Received from C: ~w~n”, [Msg])
19 end,
20
21 % stop echo-servers
22 PidB ! stop ,
23 PidC ! stop.
19/61

Sources of Multiple Messages
1 11> echo2:start(). 2 Sent42
3 Sent41
4 Received from B: 42 5 stop
6 12> echo2:start(). 7 Sent42
8 Sent41
9 Received from B: 42
10 stop
11 13> echo2:start(). 12 Sent42
13 Sent41
14 Received from C: 41 15 stop
16 14> echo2:start(). 17 Sent42
18 Sent41
19 Received from B: 42 20 stop
20/61

Sources of Multiple Messages
Multiple messages can come from the same processes
􏰟 Send several messages of the same shape and continue computing
􏰟 When receiving the responses, how can the code match them to the appropriate request?
􏰟 BIF make_ref provides globally unique reference objects (references for short) different from every other object in the Erlang system including remote nodes
􏰟 References can be used to uniquely identify messages
21/61

Sources of Multiple Messages
1 2 3 4 5 6 7 8 9
10 11 12 13
-module(echo3). -export([start/0]).
echo() -> receive
%
stop -> true
end.
continues in next
slide …
{From, Ref, Msg} ->
From ! {self(), Ref, Msg}, echo ();
22/61

Sources of Multiple Messages
1 start() ->
2 PidB = spawn(fun echo/0),
3 % sending tokens
4 Token = 42,
5 Ref = make_ref(),
6 PidB ! {self(), Ref, Token},
7 io:format(“Sent~w~n”,[Token]),
8 Token2 = 41,
9 Ref2 = make_ref(),
10 PidB ! {self(), Ref2, Token2},
11 io:format(“Sent~w~n”,[Token2]),
12 % receive messages
13 receive
14 {PidB , Ref2 , Msg} ->
15 io:format(“Received 41? ~w~n”, [Msg]) ;
16 {PidB, Ref, Msg} ->
17 io:format(“Received 42? ~w~n”, [Msg])
18
19 end, 20
21 % stop echo-servers
22 PidB ! stop.
23/61

Selective Receive
􏰟 Clauses can have guards
􏰟 Guards must be composed of terminating functions (BIFs)
1 receive
2 {Pid, Ref, N} when N>0 -> …
24/61

Timeouts
1 2 3 4 5 6
f(Pid) -> receive
{Pid, Msg} -> Msg after 3000 ->
timeout
end.
􏰟 The after part will be triggered if 3000 milliseconds have passed without receiving a message that matches the pattern.
􏰟 Other uses
sleep(T) -> receive
after T -> ok
end.
flush() -> receive
1 2 3 4 5 6 7 8 9
10 11 12
flush ()
ok end.
_ -> after 0 ->
25/61

Exercise
1 2 3 4 5 6 7
􏰟 Implement a semaphore
􏰟 Use the when clause
Template that you can start from:
-module(semaphore). -compile(export_all).
make_semaphore(Permits) -> spawn(?MODULE,semaphore,[Permits]).
% complete
􏰟 ?MODULE: macro that refers to the name of the current module
26/61

A Semaphore
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20
-module(semaphore). -compile(export_all).
make_semaphore(Permits) -> spawn(?MODULE,semaphore,[Permits]).
semaphore (0) -> receive
{From,Ref,release} -> semaphore (1)
end;
semaphore(P) when P>0 ->
receive {From,Ref,release} ->
From!{self(),Ref,ok},
semaphore(P+1); {From,Ref,acquire} ->
From!{self(),Ref,ok}, semaphore(P-1)
end.
􏰟 semaphore could be specified as a FSM
27/61

Semaphore – Print “a” before “b”
1 start() ->
2 S = make_semaphore(0),
3 spawn(?MODULE,p1,[S]),
4 spawn(?MODULE,p2,[S]).
5
6 release(S) -> % could be included in semaphore module
7 R = make_ref(),
8 S!{self(),R,release},
9 receive
10 {S,R,ok} ->
11 done
12 end.
13
14 p1(S) ->
15 io:format(“a”),
16 release(S).
17
18 p2(S) -> % acquire is inlined
19 R = make_ref(),
20 S!{self(),R,acquire},
21 receive
22 {S,R,ok} ->
23 io:format(“b”)
24 end.
28/61

Message Passing
Exceptions
Links and Monitors
29/61

Three Kinds of Exceptions
􏰟 Errors
􏰟 Ends the execution in the current process and includes a stack
trace of the last functions
􏰟 Errors are the means for a function to stop its execution when
you can’t expect the calling code to handle what just happened
􏰟 Throws
􏰟 Used for cases that the programmer can be expected to handle
(try…catch).
􏰟 Exits
􏰟 Same as errors except used to signal abnormal termination
between processes.
􏰟 More lightweight than errors in that stack trace not included
Note: try…catch actually can catch them all
30/61

Errors – Example
1 2 3 4 5 6 7 8 9
10 11
1> erlang:error(badarith).
** exception error: bad argument in an arithmetic expression 2> erlang:error(custom_error).
** exception error: custom_error
3> catch(1+a).
{’EXIT’,{badarith ,[{erlang ,’+’,[1,a],[]},
{erl_eval,do_apply,6,[{file,”erl_eval.erl”},{line,681}]}, {erl_eval,expr,5,[{file,”erl_eval.erl”},{line,434}]}, {shell,exprs,7,[{file,”shell.erl”},{line,686}]}, {shell,eval_exprs,7,[{file,”shell.erl”},{line,642}]}, {shell,eval_loop,3,[{file,”shell.erl”},{line,627}]}]}
31/61

Message Passing
Exceptions
Links and Monitors
32/61

Links
􏰟 Pid1 can be linked to Pid2 by calling link(Pid2) 􏰟 Creates a two-way link
􏰟 Terminating processes emit exit signals to all linked processes, which can terminate as well or handle the exit in some way.
􏰟 This feature can be used to build hierarchical program structures where some processes are supervising other processes, for example, restarting them if they terminate abnormally.
Note: Some comments on monitors are present at the end of these set of slides
33/61

Example
1 2 3 4 5 6
1 2 3 4 5 6 7 8 9
10 11 12 13
-module(linkmon). -compile(export_all).
myproc() -> timer:sleep(2000), exit(reason).
In the shell:
> c(linkmon). {ok,linkmon} > self(). <0.79.0 >
> spawn(fun linkmon:myproc/0). <0.75.0 >
> self().
<0.79.0 >
> link(spawn(fun linkmon:myproc/0)). true
** exception error: reason
> self().
<0.83.0>
34/61

Another Example
1 2 3 4 5 6 7 8 9
10 11 12 13
1 2 3 4 5
chain(0) -> receive
_ -> ok after 2000 ->
exit(“chain dies here”) end;
chain(N) ->
Pid = spawn(fun() -> chain(N-1) end), link(Pid),
receive
_ -> ok end.
In the shell:
1> c(linkmon).
{ok,linkmon}
2> link(spawn(linkmon ,
true
** exception error: “chain dies here”
chain ,
[3])).
35/61

Another Example (cont.)
[shell] == [3] ==
[shell] == [3] ==
[shell] == [3] ==
[shell] == [3] ==
[shell] == *dead*
*dead, error message shown* [shell] <-- restarted [2] == [1] == [0] [2] == [1] == *dead* [2] == *dead* *dead* 􏰟 After the process running linkmon:chain(0) dies, the error is propagated down the chain of links until the shell process itself dies because of it. 􏰟 The crash could have happened in any of the linked processes 􏰟 because links are bidirectional, you only need one of them to die for the others to follow suit. 36/61 On Number of Links and Linking 􏰟 Links cannot be stacked. 􏰟 Calling link/1 multiple times for the same two processes, will still create only one link between them 􏰟 A single call to unlink/1 will be enough to tear it down. 􏰟 link(spawn(Function)) or link(spawn(M,F,A)) happens in more than one step. In some cases, it is possible for a process to die before the link has been set up and then provoke unexpected behavior. 􏰟 spawn_link/1-3 spawns and links as an atomic operation 37/61 Trapping Exit Signals 􏰟 In order to be reliable, an application needs to be able to both kill and restart a process quickly. 􏰟 Links convenient for the killing part but restarting is missing. 􏰟 When a linked process terminates, it terminates with an exit reason that is sent through a special message known as an exit signal 􏰟 Eg. exit signal with exit reason "chain dies here" exit("chain dies here") 38/61 Trapping Exit Signals 􏰟 The default behaviour when a process receives an exit signal with an exit reason other than normal, is to terminate and in turn emit exit signals with the same exit reason to its linked processes. 􏰟 System processes: normal processes, except they can convert exit signals to regular messages. 􏰟 Done by calling process_flag(trap_exit, true) in a running process. 􏰟 Allows a process to react to exit signals 39/61 Chain Example Revisited Chain example with a system process at the beginning 1 1> process_flag(trap_exit , true).
2 true
3 2> spawn_link(fun() -> linkmon:chain(3) end).
4 <0.49.0 >
5 3> receive X -> X end.
6 {’EXIT’,<0.49.0>,”chain dies here”}
Description of behavior:
[shell] == [3] == [2] == [1] == [0] [shell] == [3] == [2] == [1] == *dead* [shell] == [3] == [2] == *dead* [shell] == [3] == *dead*
[shell] <-- {’EXIT,Pid,"chain dies here"} -- *dead* [shell] <-- still alive! 40/61 Kill Reason 􏰟 Acts as a special signal that can’t be trapped. 􏰟 Ensures any process you terminate with it will be dead. 􏰟 A last resort, when everything else has failed. 􏰟 As the kill reason can never be trapped, it needs to be changed to killed when other processes receive the message. 􏰟 Otherwise, every other process linked to it would in turn die for the same kill reason and would in turn kill its neighbors, and so on. 􏰟 This explains why exit(kill) looks like killed when received from another linked process. 1 > spawn_link(fun() -> exit(kill) end).
2 ** exception exit: killed
41/61

MSC for Critic Example
42/61
Judge Critic
{PID_J,{Band,Album}}
{PID_C,Answer}

Restarting Processes
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20 21 22 23
start_critic() -> spawn(?MODULE, critic, []).
judge(Pid, Band, Album) ->
Pid ! {self(), {Band, Album}},
receive
{Pid, Criticism} -> Criticism after 2000 ->
timeout end.
critic() -> receive
{From, {“Rage Against the Turing Machine”, “Unit Testify”}} -> From ! {self(), “They are great!”};
{From, {“System of a Downtime”, “Memoize”}} ->
From ! {self(), “They’re not Johnny Crash but they’re good.”
{From, {“Johnny Crash”, “The Token Ring of Fire”}} -> From ! {self(), “Simply incredible.”};
{From, {_Band, _Album}} ->
From ! {self(), “They are terrible!”}
end, critic().
43/61

Restarting Processes
1 2 3 4 5 6
1 2 3 4
1> c(linkmon).
{ok,linkmon}
2> Critic = linkmon:start_critic().
<0.47.0 >
3> linkmon:judge(Critic, “Genesis”, “The Lambda Lies Down on Broad “They are terrible!”
We now kill the Critic process
4> exit(Critic, solar_storm).
true
5> linkmon:judge(Critic, “Genesis”, “A trick of the Tail Recursion timeout
We need a “supervisor” process to keep critics alive
44/61

MSC for Critic Example
45/61
Judge Critic Restarter
{PID_J,{Band,Album}}
{PID_C,Answer}
{‘EXIT’,PID_C,Abnormal}
spawn_new_critic

Restarting Processes
1 2 3 4 5 6 7 8 9
10 11 12 13 14
1 2 3 4 5
start_critic2() -> spawn(?MODULE, restarter, []).
restarter () ->
process_flag(trap_exit , true),
Pid = spawn_link(?MODULE, critic, []), receive
{’EXIT’, Pid, normal} -> % not a crash ok;
{’EXIT’, Pid, shutdown} -> % manual termination, not a crash ok;
{’EXIT’, Pid, _} -> restarter()
end.
Problem: Pid of the critic is part of internal state, it is not known
1> c(linkmon).
{ok,linkmon}
2> linkmon:start_critic2().
<0.48.0 >
3> linkmon:judge(?????, “Genesis”, “The Lambda Lies Down on Broadw
46/61

Restarting Processes
1 2 3 4 5 6 7 8 9
10 11 12
􏰟 We can name a process, using an atom, rather than use its pid via erlang:register/2
􏰟 If a process dies, it will automatically lose its name or you can also use unregister/1
􏰟 You can get a list of all registered processes with registered/0 or a more detailed one with the shell command regs().
restarter () ->
process_flag(trap_exit , true),
Pid = spawn_link(?MODULE, critic, []), register(critic , Pid),
receive
{’EXIT’, Pid, normal} -> % not a crash ok;
{’EXIT’, Pid, shutdown} -> % manual termination, not a crash ok;
{’EXIT’, Pid, _} -> restarter()
end.
What about the judge?
47/61

Restarting a Process
1 2 3 4 5 6 7 8
judge2(Band, Album) ->
receive
{Pid, Criticism} -> Criticism
after 2000 -> timeout
end.
critic ! {self(), {Band, Album}}, Pid = whereis(critic),
48/61

Restarting a Process
1 1> linkmon:start_critic2().
2 <0.58.0 >
3 2> whereis(critic).
4 <0.59.0 >
5 3> linkmon:judge2(“Genesis”, “A trick of the Tail Recursion”).
6 “They are terrible!”
7 4> exit(whereis(critic),solar_storm).
8 true
9 5> linkmon:judge2(“Genesis”, “A trick of the Tail Recursion”).
10 “They are terrible!”
11 6> whereis(critic).
12 <0.63.0>
49/61

Race Conditions due to Shared State
􏰟 critic is stored in a shared registry
􏰟 There are processes that read it such as judge2 􏰟 And processes that write to it such as restarter 􏰟 Race conditions are therefore possible
50/61

Race Conditions due to Shared State – Example 1
1. critic ! Message
5. whereis fails 7. code crashes
2. critic receives 3. critic replies 4. critic dies
6. critic is restarted
1 judge2(Band, Album) ->
2 critic ! {self(), {Band, Album}},
3 %% critic dies at this point
4 %% register still not updated
5 Pid = whereis(critic), %% fails (returns undefined)
6 receive
7 {Pid, Criticism} -> Criticism %% undefined!=Pid
8 after 2000 ->
9 timeout
10 end.
51/61

Race Conditions due to Shared State – Example 2
1. critic ! Message
2. critic receives
3. critic replies
4. critic dies
5. critic is restarted
6. whereis picks up wrong pid 7. message never matches
1 judge2(Band, Album) ->
2 critic ! {self(), {Band, Album}},
3 %% critic dies at this point
4 %% register updated with new Pid
5 Pid = whereis(critic), %% successful (but different Pid)
6 receive
7 {Pid, Criticism} -> Criticism %% no match
8 after 2000 ->
9 timeout
10 end.
􏰟 Both may be solved by replacing the use of whereis (and Pid matching) to that of reference matching
52/61

Adding References to Messages
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19 20 21
judge2(Band, Album) ->
Ref = make_ref(),
critic ! {self(), Ref, {Band, Album}}, receive
{Ref, Criticism} -> Criticism after 2000 ->
timeout end.
critic2() -> receive
{From, Ref, {“Rage Against the Turing Machine”, “Unit Testify” From ! {Ref, “They are great!”};
{From, Ref, {“System of a Downtime”, “Memoize”}} ->
From ! {Ref, “They’re not Johnny Crash but they’re good.”};
{From, Ref, {“Johnny Crash”, “The Token Ring of Fire”}} -> From ! {Ref, “Simply incredible.”};
{From, Ref, {_Band, _Album}} ->
From ! {Ref, “They are terrible!”}
end, critic2 ().
53/61

Appendix: More on Exceptions
Appendix: Monitors
54/61

Revisiting Exceptions – How Processes Trap Them
􏰟 spawn_link(fun() ->ok end)
􏰟 Untrapped Result: Nothing
􏰟 Trapped Result: {’EXIT’, <0.61.0>, normal}
􏰟 The process exited normally, without a problem.
􏰟 spawn_link(fun() ->exit(reason) end)
􏰟 Untrapped Result: ** exception exit: reason
􏰟 Trapped Result: {’EXIT’, <0.55.0>, reason}
􏰟 The process has terminated for a custom reason.
􏰟 spawn_link(fun() ->exit(normal) end) 􏰟 Untrapped Result: Nothing
􏰟 Trapped Result: {’EXIT’, <0.58.0>, normal} 􏰟 Emulates process terminating normally.
55/61

Revisiting Exceptions
􏰟 spawn_link(fun() ->1/0 end)
􏰟 Untrapped Result:
Error in process <0.44.0> with exit value: {badarith, [{erlang, ’/’,
􏰟 Trapped Result:
{’EXIT’, <0.52.0>, {badarith, [{erlang, ’/’, [1,0]}]} 􏰟 spawn_link(fun() ->erlang:error(reason) end)
􏰟 Untrapped Result:
Error in process <0.47.0> with exit value: {reason, [{erlang, apply,
􏰟 Trapped Result:
{’EXIT’, <0.74.0>, {reason, [{erlang, apply, 2}]]}}
􏰟 Similar to 1/0.
􏰟 spawn_link(fun() ->throw(rocks) end)
􏰟 Untrapped Result:
Error in process <0.51.0> with exit value: {{nocatch, rocks}, [{erlan
􏰟 Trapped Result:
{’EXIT’, <0.79.0>, {{nocatch, rocks}, [{erlang, apply, 2}]}}
􏰟 Because the throw is never caught by a try … catch, it bubbles up into an error, which in turn bubbles up into an EXIT. Without trapping exit, the process fails.
56/61

Revisiting Exceptions – the exit/2 case
Allows a process to kill another one from a distance, safely
􏰟 exit(self(), normal)
􏰟 Untrapped Result: ** exception exit: normal
􏰟 Trapped Result: {’EXIT’, <0.31.0>, normal}
􏰟 When not trapping exits, exit(self(), normal) acts the same
as exit(normal).
􏰟 exit(spawn_link(fun() ->timer:sleep(50000) end), normal) 􏰟 Untrapped Result: nothing
􏰟 Trapped Result: nothing
􏰟 exit(spawn_link(fun() ->timer:sleep(50000) end), reason)
􏰟 Untrapped Result: ** exception exit: reason 􏰟 Trapped Result: {’EXIT’, <0.52.0>, reason}
57/61

Revisiting Exceptions – the exit/2 case
􏰟 exit(spawn_link(fun() ->timer:sleep(50000) end), kill) 􏰟 Untrapped Result: ** exception exit: killed
􏰟 Trapped Result: {’EXIT’, <0.58.0>, killed}
􏰟 exit(self(), kill)
􏰟 Untrapped Result: ** exception exit: killed 􏰟 Trapped Result: ** exception exit: killed
􏰟 spawn_link(fun() ->exit(kill) end)
􏰟 Untrapped Result: ** exception exit: killed 􏰟 Trapped Result: {’EXIT’, <0.67.0>, kill}
58/61

Monitors
􏰟 Special type of link with two differences 􏰟 they are unidirectional,
􏰟 can monitor via a registered name, and 􏰟 they can be stacked.
􏰟 Allows a process to, unobtrusively, monitor another one
􏰟 Useful for when you have multiple libraries that you call and they all need to know whether a process is alive or not
􏰟 You can stack links and remove them individually
59/61

Example
erlang:monitor/2 sets up a monitor, where the first argument is the
atom process and the second one is the pid
1 1> erlang:monitor(process, spawn(fun() -> timer:sleep(500) end)). 2 #Ref<0.0.0.77>
3 2> flush().
4 Shell got {’DOWN’,#Ref<0.0.0.77>,process,<0.63.0>,normal}
5 ok
􏰟 When monitored process goes down, send message to
monitor: {’DOWN’, MonitorReference, process, Pid, Reason}.
􏰟 The reference allows you to demonitor the process.
􏰟 Monitors are stackable, so it’s possible to take more than one
down.
􏰟 References allow you to track each of them in a unique
manner.
60/61

Example
Atomic function to spawn process while monitoring it:
1 3> {Pid, Ref} = spawn_monitor(fun() -> receive _ -> exit(boom) end 2 {<0.73.0>,#Ref<0.0.0.100>}
3 4> erlang:demonitor(Ref).
4 true
5 5> Pid ! die. 6 die
7 6> flush().
8 ok
􏰟 We demonitored the other process before it crashed hence no trace of it dying.
61/61