Concepts of programming languages Lecture 10 Wouter Swierstra Faculty of Science Information and Computing Sciences 1
Project progress Please send me a brief progress no later than Friday! Each time will be given a 15 minute slot to present their results on 26/1 – we’ll use both the lecture and lab slot. Hand in your code and report on Friday 27/1. Faculty of Science Information and Computing Sciences 2
Last time Concurrency and parallelism ▶ Why is this important? ▶ What are these two concepts? ▶ How does Erlang address these challenges? Faculty of Science Information and Computing Sciences 3
Today Erlang’s error handling Parallel programming in Erlang Faculty of Science Information and Computing Sciences 4
Erlang recap Erlang is a concurrency-oriented programming language. It is cheap to spawn new processes. These processes do not share any state, but communicate through message passing . A process can send and receive messages to any other process, using send and receive . Faculty of Science Information and Computing Sciences 5
area({square, X}) -> X * X; area({rectangle, X,Y}) -> X * Y. 8>test:area({rectangle,3,4}). 12 9>test:area({circle,2}). **exception error: no function clause matching test:area({circle,2}) in (test.erl, line 16) Example: area of a shape We can now test such functions: Faculty of Science Information and Computing Sciences 6
Question: Why is this a good idea? Why is it not? area({square, X}) -> X * X; area({rectangle, X,Y}) -> X * Y; area(_) -> 0. Erlang Instead of crashing, we could choose to adapt our function to try and cope with unexpected inputs: Now unexpected calls, such as test:area({circle, 2}) will no longer crash the process, but return a dummy result instead. Faculty of Science Information and Computing Sciences 7
area({square, X}) -> X * X; area({rectangle, X,Y}) -> X * Y; area(_) -> 0. Erlang Instead of crashing, we could choose to adapt our function to try and cope with unexpected inputs: Now unexpected calls, such as test:area({circle, 2}) will no longer crash the process, but return a dummy result instead. Question: Why is this a good idea? Why is it not? Faculty of Science Information and Computing Sciences 7
Time passes… You add many new difgerent functions manipulating shapes. And you decide to extend the supported shapes with circles after all. At some point, we notice that things don’t work as expected for our new shapes – we are still silently producing a bogus answer. And we end up writing lots of code to fjx this elsewhere. Faculty of Science Information and Computing Sciences 8
Error handling ▶ Handling errors can account to more than 60% of the code base – it is expensive to develop and maintain. ▶ Typically, error handling code is poorly tested as most testing efgort is spent on handling the success scenarios. About two-thirds of system crashes are caused by bugs in the error-handling code. Faculty of Science Information and Computing Sciences 9
Let it crash! The Erlang philosophy is that stopping a malfunctioning process is better than let it continue to efgect the overall system. As the system is composed of many difgerent processes, terminating one process should (hopefully) not compromise the entire system’s integrity. Instead of trying to prevent errors, focus on how to detect and recover from errors. Faculty of Science Information and Computing Sciences 10
Let it crash! This sounds like a brazen strategy for error handling, but: ▶ there is no shared memory between processes; ▶ there is no mutable data; We can isolate the failure to a single process. One user or client may observe the failed process, but the entire system should keep going. Faculty of Science Information and Computing Sciences 11
Supervisor processes One way to ensure that the system remains stable would be through a supervisor process that detects the failure of other processes. Crucially, when other processes fail, these failures should not cause the supervisor to fail. This is very ‘coarse-grained’ error handling on the process level (the big picture), rather than trying to fjx smaller problems that might occur in individual functions. What language features does Erlang provide for error handling on the process level? Faculty of Science Information and Computing Sciences 12
Terminology ▶ Two processes may be linked . If one process crashes, its linked processes are notifjed. ▶ The set of process linked to P is referred to as the link set . ▶ One directional links are refgered to as monitors . ▶ Communication is done through message passing or error signals. We’ve seen messages already; error signals are sent automatically to linked processes when a process terminates. ▶ Error signals are of the form {'EXIT', Pid, Why} describing what process ended and why it ended. ▶ Any process can call Exit(Why) to terminate and notify its linked processes. Faculty of Science Information and Computing Sciences 13
Creating links Links are created manually using the link function. The call link(Pid) links the current process to the process with id Pid . Processes may be linked with zero, one, or many other processes in this fashion. Faculty of Science Information and Computing Sciences 14
Error handling When a we receive an error signal from a linked process, two things may happen: ▶ if the process is not a system process , it dies and notifjes all its linked processes. ▶ if the process is a system process, we can stop the propagation of the signal. Any process can become a system process using the process_flag function; all spawned processes are not system processes by default. Faculty of Science Information and Computing Sciences 15
Creating monitors and links Simply calling spawn will not link the new process. There are variants spawn_link and spawn_monitor than create links/monitors. ▶ Using functions like link or monitor we can create bidirectional or unidirectional links between processes explicitly. ▶ We can destroy links using, unlink and unmonitor functions. ▶ We can crash by calling exit . Faculty of Science Information and Computing Sciences 16
Linking processes We can trigger code to run when another program exits by calling on_exit(Pid,Function) . That way, when a linked process dies, we can restart it or take suitable action. Faculty of Science Information and Computing Sciences 17
5 > Pid = spawn( fun () ->receive N -> 1 / N end end ). 6 > test:on_exit(Pid, fun (Why) -> io:format("***exit: ~p\n",[Why]) end ). 7 > Pid ! 1. *** exit: normal 1 8 > Pid ! 0. = ERROR REPORT ==== 25 - Apr - 2012::19:57:07 === Error in process < 0.60.0 > with exit value: {badarith,[{erlang,'/',[1,0],[]}]} *** exit: {badarith,[{erlang,'/',[1,0],[]}]} 0 Example: division server Faculty of Science Information and Computing Sciences 18
Live together, die together If we want to split a problem into smaller pieces to achieve parallelism, we want the whole process to fail if the subprocesses do. This is easy to achieve by: ▶ spawning new worker processes; ▶ linking the new processes with the parent process. If any worker process dies, all the processes die. Faculty of Science Information and Computing Sciences 19
There is one problem: upon restarting you may have a new pid – how will anyone ever communicate with the restarted server? keep_alive(Fun) -> Pid = spawn(Fun), on_exit(Pid, fun (_) -> keep_alive(Fun) end ). Eternal threads Using functions like on_exit we can restart a process whenever it dies. This is particularly useful for servers that should never go down. Faculty of Science Information and Computing Sciences 20
keep_alive(Fun) -> Pid = spawn(Fun), on_exit(Pid, fun (_) -> keep_alive(Fun) end ). Eternal threads Using functions like on_exit we can restart a process whenever it dies. This is particularly useful for servers that should never go down. There is one problem: upon restarting you may have a new pid – how will anyone ever communicate with the restarted server? Faculty of Science Information and Computing Sciences 20
The process registry There is a global process registry that lets you associate names (or rather, atoms) with pids. ▶ register(Name,Pid) - enters a process in the registry; ▶ whereis(Name) - looks up a process in the registry; ▶ unregister(Name) - remove a process from the registry. Faculty of Science Information and Computing Sciences 21
divider() -> keep_alive( fun () -> register(divider,self()), receive N -> io:format(..., [1 / N]) end end ). > divider ! 0. =ERROR REPORT=== .... > divider ! 2. 1 divided by 2 is 0.5 Always alive divider process Now we can always send messages to this process, even after it crashes. Faculty of Science Information and Computing Sciences 22
Process architecture Deciding how to split up your problem across processes is not easy. It requires skill and experience – similar to splitting up code across objects, or choosing precise algebraic data types to model some domain. The good news is: it is cheap (both in time and syntax) to create new threads in Erlang! Faculty of Science Information and Computing Sciences 23
Recommend
More recommend