Error Handling in Erlang

In Erlang we do exactly the opposite. We build our applications in two parts: a part that solves the problem and a part that corrects errors if they have occurred.

The part that solves the problem is written with as little defensive code as possible; we assume that all arguments to functions are correct and the programs will execute without errors.

The part that corrects errors is often generic, so the same error-correcting code can be used for many different applications. (Armstrong 2013, 201)

Process Types

There are two types of processes: normal processes and system processes. spawn (see Concurrent Erlang) creates a normal process. A normal process can become a system process by evaluating the BIF process_flag(trap_exit, true).

If your process is set to trap exits, through a call to process_flag(trap_exit, true), these error reports arrive as messages, rather than just killing your process. (Laurent 2017, 100)

Exit signals can be trapped by calling the process_flag(trap_exit, true) function. This converts exit signals into messages of the form {'EXIT', Pid, Reason}, where Pid is the process identifier of the process that has died and Reason is the reason it has terminated. (Cesarini and Vinoski 2016, 36)

Reason Trapping Exits Not Trapping Exits
normal {'EXIT', Pid, normal} Nothing happens
kill Terminates with reason killed Terminates with reason killed
Other {'EXIT', Pid, Other} Terminates with reason Other

Propagation Semantics

  • Processes can trap exit signals by calling the BIF process_flag(trap_exit, true).
  • Once trapped, the errors are saved in the mailbox.
erlang_error_trapping.png

Pid2 functions as a firewall, stopping errors from propagating to other processes in the system.

Link

Linking processes can be thought as an extension of the error handling presented when dealing with Sequential Erlang. We can, if necessary, catch exceptions early in the sequential code, that still won't make our application fault tolerant.

For one process to observe another, we must create a link or monitor between the processes. If the linked or monitored processes dies, the observing process is informed.

Observing processes work transparently across machine boundaries, so a process running on one machine can monitor the behavior of a process run- ning on a different machine. This is the basis for programming fault-tolerant systems. (Armstrong 2013, 200)

To create links, we call the primitive link(Pid), which creates a link between the calling process and Pid. So, if Pid1 calls link(Pid2), a link is created between Pid1 and Pid2.

  Pid1 = link(Pid2)
  • link/1 creates a bi-directional link between the process calling the BIF (Pid1) and the process linked (Pid2).
  • spawn_link/3 will yield the same result as spawn/3 followed by link/1, only that will do so atomically.
  • unlink/1 removes the link to Pid.
erlang_process_link.png

Link Set

The link set of a process P is the set of processes that are linked to P.

erlang_link_set.png

Groups of Processes That All Die Together

In the previous example, if a single process from the Link Set fails, all the processes from the Set also fail in cascate. Ultimately, the error signals propagate to all the linked processes, and the entire group of linked processes dies.

erlang_link_set_cascade.png

When an Erlang process fails, it sends an explanation to other processes that are linked to it in the form of a tuple. The tuple contains the atom EXIT, the Pid of the failed process, and the error as a complex tuple. (Laurent 2017, 100)

Exit Signals

  • A process termination can be normal or abnormal.
    • Normal: When there is no more code to execute.
    • Abnormal: Initiated in case of a runtime error, receiving an exit signal when not trapping exits, or by calling the exit BIFs.
  • When a process terminates, it sends a signal to all process its linked to. The exit signal will contain the following information:

    Field Description
    Sender Identifier The process or port identifier of the process or port that terminated.
    Receiver Identifier The process or port identifier of the process or port which the exit signal is sent to.
    The link flag This flag will be set indicating that the exit signal was sent due to a link.
    exit reason  
erlang_error_exit_signals.png

Monitors

Monitors are similar to links but with several significant differences.

  • Monitors are unidirectional.
  • Monitors have an identity given by an Erlang reference, which is a unique value returned by the call to erlang:monitor/2.
  • A process Pid2 can monitor another process Pid1 by calling the BIF erlang:monitor(process, Pid2).
erlang_monitor.png
  • When a monitored process dies, a "down" message (not an exit signal) is sent to the monitoring process.
  • Attempting to monitor a nonexistent process results in a {'DOWN', Reference, process, Pid, Reason} message with reason noproc.
erlang_monitor_error.png

References:

Armstrong, Joe. 2013. “Programming Erlang: Software for a Concurrent World.”
Cesarini, Francesco, and Steve Vinoski. 2016. Designing for Scalability with Erlang/Otp: Implement Robust, Fault-Tolerant Systems. O’Reilly Media, Inc.
Laurent, Simon St. 2017. Introducing Erlang: Getting Started in Functional Programming. O’Reilly Media, Inc.