Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Erlang error handling


May 13, 2021 Erlang


Table of contents


Erlang error handling

Before we discuss the details of oversight and error handling, let's take a look at the termination process of the Erlang process, or Erlang's term exit.

The end of the process executing exit or running all the code is considered a normal termination of the process.

A process is called an exception termination because it triggers run-time errors (for example, zeroing, error matching, calling a function that does not exist, and so on). P rocess execution exit (note that Reason here is a value other than normal) termination is also known as abnormal termination.

One Erlang process can establish a connection to another Erlang process. I f a process calls link (Other_Pid), it creates a two-way connection between itself and the Othre_Pid process. W hen a process ends, it sends a signal to all processes connected to it.

This signal carries the process identifier of the process and the reason why the process ended.

When a process receives a signal that the process is exiting normally, it is ignored directly by default.

However, if the process receives a signal that terminates abnormally, the default action is:

  • The process receiving the abnormal termination signal ignores all messages in the message queue
  • Kill yourself
  • Pass the same error message to all the processes connected to it.

So you can connect all the processes of the same transaction by connecting. I f one of the processes terminates abnormally, all processes in the transaction are killed. I t is precisely because in the actual production process, there is often a need to create a process to connect with it at the same time, so there is such a built-in function spawn_link, unlike spawn, which creates a new process at the same time as the new process and the creator to establish a connection.

Here's another implementation of the ping pong example, which terminates the "pong" process via a connection:

-module(tut20).

-export([start/1,  ping/2, pong/0]).

ping(N, Pong_Pid) ->
    link(Pong_Pid),
    ping1(N, Pong_Pid).

ping1(0, _) ->
    exit(ping);

ping1(N, Pong_Pid) ->
    Pong_Pid ! {ping, self()},
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,
    ping1(N - 1, Pong_Pid).

pong() ->
    receive
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    end.

start(Ping_Node) ->
    PongPID = spawn(tut20, pong, []),
    spawn(Ping_Node, tut20, ping, [3, PongPID]).
(s1@bill)3> tut20:start(s2@kosken).
Pong received ping
<3820.41.0>
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong

As in the previous code, both processes of the ping pong program are still created in the start/1 function, and the "ping" process is established on a separate node. B ut here are some minor changes, using the built-in function link. E xit is called at the end of the ping, causing a termination signal to be passed to the "pong" process, causing the "pong" process to terminate.

You can also modify the default behavior of a process when it receives an abnormal termination signal to prevent the process from being killed. T hat is, all signals are converted into normal messages added to the message queue of the signal receiving process in the format of 'EXIT', FromPID, Reason. W e can set it up by following the code:

process_flag(trap_exit, true)

There are other process flags that you can use, see erlang (3). S tandard user programs generally do not need to change the default processing behavior of processes for signals, but this interface is necessary for managers in OTP. T he ping pong program is modified to print the information when the output process exits:

-module(tut21).

-export([start/1,  ping/2, pong/0]).

ping(N, Pong_Pid) ->
    link(Pong_Pid), 
    ping1(N, Pong_Pid).

ping1(0, _) ->
    exit(ping);

ping1(N, Pong_Pid) ->
    Pong_Pid ! {ping, self()},
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,
    ping1(N - 1, Pong_Pid).

pong() ->
    process_flag(trap_exit, true), 
    pong1().

pong1() ->
    receive
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong1();
        {'EXIT', From, Reason} ->
            io:format("pong exiting, got ~p~n", [{'EXIT', From, Reason}])
    end.

start(Ping_Node) ->
    PongPID = spawn(tut21, pong, []),
    spawn(Ping_Node, tut21, ping, [3, PongPID]).
(s1@bill)1> tut21:start(s2@gollum).
<3820.39.0>
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
pong exiting, got {'EXIT',<3820.39.0>,ping}