570 likes | 714 Views
Erlang Programming. Session 4. Concurrent Programming Processes. Messages. Process pattern. Process aliases. Timeouts. Example: Client-Server model. Processes. Concurrent activities are modeled using light-weight processes. Many processes may be running simultaneously.
E N D
Session 4 • Concurrent Programming • Processes. • Messages. • Process pattern. • Process aliases. • Timeouts. • Example: Client-Server model
Processes • Concurrent activities are modeled using light-weight processes. • Many processes may be running simultaneously. • The identity of the newly created process is returned from the BIF spawn/3: • Code run by PidA: PidB = spawn(Mod, Func, Args) PidA PidA PidB
activity(Joe,75,1024) Erlang Example Creating a new process using spawn -module(ex3). -export([activity/3]). activity(Name,Pos,Size) -> ………… Pid = spawn(ex3,activity,[Joe,75,1024])
Processes spawn(Module, Function, Args). • Creates a new process, i.e. another thread of execution, starting in the function call given by the arguments. • Example: spawn(m, f, [1,2,3]) creates a process starting in m:f(1,2,3).
Processes • The function used in spawn/3 must be exported. • A process will terminate when there is no more code to execute.
Processes • Messages are sent using “!” (Sometimes called bang). Pid ! Msg • Msg is any legal Erlang term • Pid is of type process identifier (most probably returned by spawn)
Processes • Processes communicates by sending and receiving messages hello PidA PidB PidB ! hello receive hello -> ... end
Processes • The BIF self/0 returns the process identifier of the process calling it • Try it in the shell.
Example: Area server -module(area_server0). -export([loop/0]). loop() -> receive {rectangle, Width, Ht} -> io:format("Area of rectangle is ~p~n", [Width * Ht]), loop(); {circle, R} -> io:format("Area of circle is ~p~n" , [3.14159 * R * R]), loop(); Other -> io:format("I don't know what the area of a ~p is ~n" ,[Other]), loop() end.
Example: Area server 1> Pid = spawn(fun area_server0:loop/0). <0.36.0> 2> Pid ! {rectangle, 6, 10}. Area of rectangle is 60 {rectangle,6,10} 3> Pid ! {circle, 23}. Area of circle is 1661.90 {circle,23} 4> Pid ! {triangle,2,4,5}. I don't know what the area of a {triangle,2,4,5} is {triangle,2,4,5}
Transparent Distribution B ! Msg C ! Msg Erlang Run-Time System Erlang Run-Time System network
Process Pattern start() -> spawn(mymod, init, [...]). init() -> <initializations>, loop(...). loop(...) receive stop -> true; %%The process stops here Pattern1 -> <actions>, loop(...); ... PatternN -> <actions>, loop(...) end.
Process example (echo process) A process that receives a message and returns it back to the sender. -module (echo). -export ([start/0, init/0]). start() -> spawn(echo, init, []). init() -> loop(). loop() -> receive stop -> true; {Pid, Msg} -> Pid ! {self(), Msg}, loop() end.
Process example (Testing echo) -module(test_echo). -export([start/1, init/1]). start(N)-> spawn(test_echo, init, [N]). init(N)-> Epid = echo:start(), loop(N, Epid). loop(0, Epid) -> Epid ! stop, io:format("Echo passed this test ~n", []); loop(N, Epid) -> Epid ! {self(), N}, io:format("Sending message, N= ~p~n", [N]), receive stop -> true; {Epid, N} -> loop(N-1, Epid) end.
Process example (echo process) -module (echo). -export ([start/0, init/0]). start() -> spawn(echo, init, []). init() -> loop(). loop()-> receive stop -> io:format("I got message ~p from process ~p~n", [stop, unknown]), true; {Pid, Msg} -> io:format("I got message ~p from process ~p~n", [Msg, Pid]), Pid ! {self(), Msg}, loop() end
Process aliases • To use a name instead of process ID, use the BIF register/2 register(Alias, Pid) • Any process can send a message using the alias. register(iti, Pid), … … iti ! hello, • Sending a message to a non-existant alias, fails.
Timeouts • To be able to program a process that does not wait forever for a message to arrive. receive hello -> io:format(“hi”, []) after 1000 -> %1000 millisec true end.
Timeouts • test_echo with timeouts (in case the echo process dies). loop(N, Epid) -> Epid ! {self(), N}, receive stop -> true; {Epid, N} -> loop(N-1, Epid) after 1000 -> io:format(“Echo timed out”) end.
Example: Client-Server model server.erl PidA • Message protocol: {request, ClientPid, alloc} {reply, Resource} {request, ClientPid, {free, Resource}} {reply, ok} server PidB PidC hw.erl
Client-Server (cont.) • Server Code -module(server). -export([start/0]). -export([alloc/0, free/1]). -export([init/0]). start() -> spawn(server, init, []). init() -> register(server, self()), Resources = hw:get_resources(), loop(Resources).
Client-Server (cont.) loop(ResDB) -> receive {request, From, alloc} -> {Resources, NewResDB} = hw:alloc(ResDB}, From ! {reply, Resource}, loop(NewResDB}; {requet, From, {free, Resource}}-> From ! {reply, ok}, loop(hw:free(Resource, ResDB)); _Other loop(ResDB) end.
Client-Server (cont.) • Client functions: alloc() -> call(alloc). free(Resource) call({free, Resource}). call(Request) -> server ! {request, self(), Request}, receive {reply, Reply} -> Reply end.
Lesson 4 • Links. • Exit signals. • Trapping exit signals. • Robust systems. • Monitors.
Links • Processes can be linked together using the BIF link(Pid). or spawn_link(Module, Function, Args). Links are removed using: unlink(Pid). PidB PidA
PidB PidA Exit Signals • When a process terminates, exit signals are sent over all links connected to the terminating process EXIT
Exit Signals • A process can terminate: • normally: (when it has no more code to execute). • abnormally: due to run-time error. • abnormally: due to execution of BIF causing termination exit(Reason). • Note: a signal is not a message.
Exit signals • When a process terminates normally (no more code to execute), the emitted exit-signals are ignored by the linked processes. • When the process terminates abnormally, linked process will notice the emitted exit signals… … and terminate themselves
Exit Signals When a process terminates, an exit signal is sent to all linked processes … and the termination is propagated
PidB PidA Exit Signals • The BIF exit/2 emits an exit-signal to a process even if there is no link between them. exit(DestPid, Reason). • PidA doesn’t terminate, it just terminates PidB EXIT
Trapping exit signals • To protect itself from exit signals, a process uses the BIF: process_flag(trap_exit, true). • Then, incoming exit signals will be transformed to a message and put in the message que in the format: {‘EXIT’, Pid, Reasons}
Trapping exit signals • Trapping exit causes any exit signal to be transformed into a message even signals occuring due to normal termination of linked processes. • When a process PidA is linked to PidB, PidB is trapping exit and PidA terminates normally (no more code to execute), PidB receives the following message: {‘EXIT’, Pid, Reasons} • Remember what happens in the above case when PidB is not trapping exit? • PidB just receives the exit signal but ignores it.
Trapping exit signals • Processes that traps exit can also be killed by sending them an exit signal with reason kill • The process terminates with the reason killed
Client-Server (robust) • Server Code -module(server). -export([start/0]). -export([alloc/0, free/1]). -export([init/0]). start() -> spawn(server, init, []). init() -> process_flag(trap_exit, true), register(server, self()), Resources = hw:get_resources(), loop(Resources).
Client-Server (cont.) loop(ResDB) -> receive {request, From, alloc} -> {Resources, NewResDB} = hw:alloc(ResDB}, link(From), From ! {reply, Resource}, loop(NewResDB}; {requet, From, {free, Resource}}-> unlink(From), From ! {reply, ok}, loop(hw:free(Resource, ResDB)); {‘EXIT’, From, _Reason}-> <do something> loop(NewResDB); _Other -> loop(ResDB) end.
Monitors • The BIF erlang:monitor/2 offers a way to monitor another process. • The monitor is not bi-directional like links. MRef = erlang:monitor(process, Process). Process = pid() | atom() documentation style • If the process terminates the calling process will receive a {‘DOWN’, MRef, process, Object, Reason}
Exercises • Write a process that upon receiving a certain message terminates normally and when receiving another message terminates ubnormaly. loop() -> receive stop -> io:format("Process stopped~n"); error -> io:format("Process will terminate ubnormally~n"), abc = abcd; Msg -> io:format("Message ~p received ~n", [Msg]), loop() end.
Exercises • Write a function that starts another process • pmon:start(SupervisedPID). • The process shall use a link to monitor the supervised process. • The monitoring process shall print a message if the supervised process terminates abnormally. • The monitoring process should terminate whenever the supervised process does, regardless of the reason.
Solution -module(pmon). -export([start/1, init/1]). start(Pid)-> spawn(pmon, init, [Pid]). init(Pid)-> process_flag(trap_exit, true), link(Pid), loop(Pid). loop(Pid)-> receive {'EXIT', Pid, normal}-> ok; {'EXIT', Pid, Reason} -> io:format("Process ~p terminated abnormally, reason: ~p~n", [Pid, Reason]); _Other -> loop(Pid) end.
Master and Slaves • Write a module ms that has the following interfaces: • start(N): starts a master process and tells it to start N slave processes. Register the master as master. • to_slave(Message, N): Sends a message to the master and tell it to relay the message to slave N. • The slave should exit if it receives the message die. • The master should restart the dead slave and restarts it.
Master and slaves • ms:start(4). • true • ms:to_slave(hello, 2). • {hello, 2} • Slave 2 got message hello. • ms:to_slave(die, 3). • {die, 3} • master restarting dead slave 3.
Solution -module(ms). -export([start/1, to_slave/2]). -export([master_init/1, slave_init/1]). start(N)-> spawn(ms, master_init, [N]). to_slave(Msg, N)-> master ! {to_slave, self(), Msg, N}. master_init(N)-> register(master, self()), process_flag(trap_exit, true), Slaves = start_slaves(N, []), master_loop(Slaves).
Solution start_slaves(0, Slaves)-> Slaves; start_slaves(N, Slaves) -> start_slaves(N-1, [spawn_link(ms, slave_init, [N])|Slaves]).