class: center, middle
Pre-alpha Kottans courses
class: center,middle
class: center,middle
class: center
--- # ProcessesProcesses in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
Processes in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
- they have their own garbage collector (GC) per process
Processes in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
- they have their own garbage collector (GC) per process
- they have no shared state
Processes in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
- they have their own garbage collector (GC) per process
- they have no shared state
- they are cheap
Processes in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
- they have their own garbage collector (GC) per process
- they have no shared state
- they are cheap
- they are VM-based, not OS-based
Processes in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
- they have their own garbage collector (GC) per process
- they have no shared state
- they are cheap
- they are VM-based, not OS-based
- they can communicate via messages
Processes in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
- they have their own garbage collector (GC) per process
- they have no shared state
- they are cheap
- they are VM-based, not OS-based
- they can communicate via messages
- they can spawn and be spawned by another process
Processes in Erlang are built-in concept. Here are few key notes about processes in Erlang:
- they are isolated
- they have their own garbage collector (GC) per process
- they have no shared state
- they are cheap
- they are VM-based, not OS-based
- they can communicate via messages
- they can spawn and be spawned by another process
- in case of fault they can send message to spawner with fault description
class: center,middle
Let's spawn some processes. This is hello_server.erl
, our precisious server.
-module(hello_server).
-export([loop/0]).
loop() ->
loop().
Now let's compile and spawn it!
1> c(hello_server).
{ok, hello_server}
2> Client = spawn(fun hello_server:loop/0).
<0.98.0>
3> i(). % this is like ps in *nix
Pid Initial Call Heap Reds Msgs
Registered Current Function Stack
<0.0.0> otp_ring0:start/2 610 5895 0
init init:loop/1 2
% ...
<0.98.0> erlang:apply/2 233 20621685 0
hello_server:loop/0 0
% here is our server
Total 40075 43316007 1
239
Spawn knows about other nodes. You can spawn function on other node.
Launch node 1:
node1> erl -sname node1 -setcookie secret
Launch node 2:
node2> erl -sname node2 -setcookie secret
Spawn knows about other nodes. You can spawn function on other node.
On node1:
1> RemoteClient = spawn('node2@localhost', fun hello_server:loop/0).
<10095.72.0>
On node2:
1> i().
Pid Initial Call Heap Reds Msgs
Registered Current Function Stack
<0.72.0> net_kernel:spawn_func/6 233 12425589 0
hello_server:loop/0 0
Total 68150 12439263 0
297
class: center,middle
How do processes communicate if they are isolated? They send messages! Each process has his own mailbox. Let's take a look.
How do processes communicate if they are isolated? They send messages! Each process has his own mailbox. Let's take a look.
On node 1:
2> RemoteClient ! {hello, world}
{hello, world}
How do processes communicate if they are isolated? They send messages! Each process has his own mailbox. Let's take a look.
On node 1:
2> RemoteClient ! {hello, world}
{hello, world}
On node 2 take PID from i() output and:
2> Pid = list_to_pid("<0.72.0>").
<0.72.0>
3> process_info(Pid, message_queue_len).
{message_queue_len,1}
4> process_info(Pid, messages).
{messages,[{hello,world}]}
class: center,middle
So we need not only receive messages but also receive them. We can do it by tricky way like
Mailbox = process_info(self(), messages),
[H|T] = Mailbox. % KILL ME PLS
But on last lecture we have learnt less tricky way, the glorious receive
function. So let's create com_server.erl
-module(com_server).
-export([listen/0]).
listen() ->
receive
{hello, Name} ->
io:format("Hello, ~s~n", [Name]),
listen();
{bye, Name} ->
io:format("Bye, ~s~n", [Name]),
listen();
_ ->
listen()
end.
This will create listen loop.
Let's try spawn our process and send message to it.
1> c(com_server).
{ok, com_server}
2> Pid = spawn(fun com_server:listen/0).
<0.71.0>
3> Pid ! {hello, "World"}.
Hello, World
ok
class: center,middle
In general there are two ways to do this - normal and with exception.
-module(crash_server).
-export([loop/0]).
loop() ->
receive
{From, type1, Message} ->
% ...
loop();
{From, type2, Message} ->
% ...
loop();
{From, terminate} ->
From ! {self(), ok};
{From, exit} ->
exit(failure); % if catched, transformed to message {'EXIT', failure}
_ ->
loop()
end.
class: center,middle
Sometimes you need to pass some of messages first and all other after. This is the way how it's implemented in selective.erl
-module(selective).
-export([important/0]).
important() ->
receive
{Priority, Message} when Priority > 10 ->
[Message | important()]
after 0 ->
normal()
end.
normal() ->
receive
{_, Message} ->
[Message | normal()]
after 0 ->
[]
end.
Let's take a look, how it works.
1> c(selective).
{ok,selective}
2> self() ! {15, high}, self() ! {7, low}, self() ! {1, lowest}, self() ! {17, highest}.
{17,high}
3> process_info(self(), messages).
{messages,[{15,high},{7,low},{1,lowest},{17,highest}]}
4> selective:important().
[high,highest,low,lowest]
Special note: Please rewrite this module to take care of ranges of priorities without numbers, just by atoms:
- lowest
- low
- high
- highest
class:center,middle
Processes can be registered to avoid entering or passing PIDs. Take a look at example registered.erl
:
Processes can be registered to avoid entering or passing PIDs. Take a look at example registered.erl
:
-module(registered).
-export([start/0, ping/1, pong/0]).
ping(0) ->
pong ! finished,
io:format("Ping finished~n", []);
ping(N) ->
pong ! {ping, self()},
receive
pong ->
io:format("Ping received pong~n", [])
end,
ping(N - 1).
% continued on next page
% beginning on previous page
pong() ->
receive
finished ->
io:format("Pong finished~n", []);
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pong()
end.
start() ->
register(pong, spawn(registered, pong, [])),
spawn(registered, ping, [3]),
ok.
Let's test them in shell.
1> c(registered).
{ok,registered}
2> registered:start().
Pong received ping
<0.151.0>
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Ping finished
Pong finished
class: center,middle
Sometimes it's needed to know when your spawned process crashed. That's why process links exists. Take a look at module linked.erl
-module(linked).
-export([chain/1]).
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.
Let's test it in the shell.
1> c(linked).
{ok,linked}
2> link(spawn(linked, chain, [3])).
true
** exception error: "chain dies here"
What happened here? chain(3)
linked chain(2)
, chain(2)
linked chain(1)
, chain(1)
linked chain(0)
. chain(0)
received timeout and throwed exception. It was populated upper and upper because of link, so all chains died up to shell. Shell was restarted by supervisor.
class: center,middle
class: center,middle