Erlang Reference Manual

User's Guide

Version 9.3.3.15

Chapters

13 Processes

13.1  Processes

Erlang is designed for massive concurrency. Erlang processes are lightweight (grow and shrink dynamically) with small memory footprint, fast to create and terminate, and the scheduling overhead is low.

13.2  Process Creation

A process is created by calling spawn():

spawn(Module, Name, Args) -> pid()
  Module = Name = atom()
  Args = [Arg1,...,ArgN]
    ArgI = term()

spawn() creates a new process and returns the pid.

The new process starts executing in Module:Name(Arg1,...,ArgN) where the arguments are the elements of the (possible empty) Args argument list.

There exist a number of different spawn BIFs:

13.3  Registered Processes

Besides addressing a process by using its pid, there are also BIFs for registering a process under a name. The name must be an atom and is automatically unregistered if the process terminates:

BIF Description
register(Name, Pid) Associates the name Name, an atom, with the process Pid.
registered() Returns a list of names that have been registered using register/2.
whereis(Name) Returns the pid registered under Name, or undefined if the name is not registered.

Table 13.1:   Name Registration BIFs

13.4  Process Termination

When a process terminates, it always terminates with an exit reason. The reason can be any term.

A process is said to terminate normally, if the exit reason is the atom normal. A process with no more code to execute terminates normally.

A process terminates with an exit reason {Reason,Stack} when a run-time error occurs. See Exit Reasons.

A process can terminate itself by calling one of the following BIFs:

  • exit(Reason)
  • erlang:error(Reason)
  • erlang:error(Reason, Args)

The process then terminates with reason Reason for exit/1 or {Reason,Stack} for the others.

A process can also be terminated if it receives an exit signal with another exit reason than normal, see Error Handling.

13.5  Signals

All communication between Erlang processes and Erlang ports is done by sending and receiving asynchronous signals. The most common signals are Erlang message signals. A message signal can be sent using the send operator!. A received message can be fetched from the message queue by the receiving process using the receive expression.

Synchronous communication can be broken down into multiple asynchronous signals. An example of such a synchronous communication is a call to the erlang:process_info/2 BIF when the first argument does not equal the process identifier of the calling process. The caller sends an asynchronous signal requesting information, and then blocks waiting for the reply signal containing the requested information. When the request signal reaches its destination, the destination process replies with the requested information.

Sending Signals

There are many signals that processes and ports use to communicate. The list below contains the most important signals. In all the cases of request/reply signal pairs, the request signal is sent by the process calling the specific BIF, and the reply signal is sent back to it when the requested operation has been performed.

message
Sent when using the send operator!, or when calling one of the erlang:send/2,3 or erlang:send_nosuspend/2,3 BIFs.
link
Sent when calling the link/1 BIF.
unlink
Sent when calling the unlink/1 BIF.
exit
Sent either when explicitly sending an exit signal by calling the exit/2 BIF, or when a linked process terminates. If the signal is sent due to a link, the signal is sent after all directly visible Erlang resources used by the process have been released.
monitor
Sent when calling one of the monitor/2,3 BIFs.
demonitor
Sent when calling one of the demonitor/1,2 BIFs, or when a process monitoring another process terminates.
down
Sent by a monitored process or port that terminates. The signal is sent after all directly visible Erlang resources used by the process or the port have been released.
change
Sent by the clock service on the local runtime system, when the time offset changes, to processes which have monitored thetime_offset.
group_leader
Sent when calling the group_leader/2 BIF.
spawn_request/spawn_reply, open_port_request/open_port_reply
Sent due to a call to one of the spawn/1,2,3,4, spawn_link/1,2,3,4, spawn_monitor/1,2,3,4, spawn_opt/2,3,4,5, spawn_request/1,2,3,4,5, or erlang:open_port/2 BIFs. The request signal is sent to the spawn service which responds with the reply signal.
alive_request/alive_reply
Sent due to a call to the is_process_alive/1 BIF.
garbage_collect_request/garbage_collect_reply, check_process_code_request/check_process_code_reply, process_info_request/process_info_reply
Sent due to a call to one of the garbage_collect/1,2, erlang:check_process_code/2,3, or process_info/1,2 BIFs. Note that if the request is directed towards the caller itself and it is a synchronous request, no signaling will be performed and the caller will instead synchronously perform the request before returning from the BIF.
port_command, port_connect, port_close
Sent by a process to a port on the local node using the send operator!, or by calling one of the send() BIFs. The signal is sent by passing a term on the format {Owner, {command, Data}}, {Owner, {connect, Pid}}, or {Owner, close} as message.
port_command_request/port_command_reply, port_connect_request/port_connect_reply, port_close_request/port_close_reply, port_control_request/port_control_reply, port_call_request/port_call_reply, port_info_request/port_info_reply
Sent due to a call to one of the erlang:port_command/2,3, erlang:port_connect/2, erlang:port_close/1, erlang:port_control/3, erlang:port_call/3, erlang:port_info/1,2 BIFs. The request signal is sent to a port on the local node which responds with the reply signal.
register_name_request/register_name_reply, unregister_name_request/unregister_name_reply, whereis_name_request/whereis_name_reply
Sent due to a call to one of the register/2, unregister/1, or whereis/1 BIFs. The request signal is sent to the name service, which responds with the reply signal.
timer_start_request/timer_start_reply, timer_cancel_request/timer_cancel_reply
Sent due to a call to one of the erlang:send_after/3,4, erlang:start_timer/3,4, or erlang:cancel_timer/1,2 BIFs. The request signal is sent to the timer service which responds with the reply signal.

The clock service, the name service, the timer service, and the spawn service mentioned previously are services provided by the runtime system. Each of these services consists of multiple independently executing entities. Such a service can be viewed as a group of processes, and could actually be implemented like that. Since each service consists of multiple independently executing entities, the order between multiple signals sent from one service to one process is not preserved. Note that this does not violate the signal ordering guarantee of the language.

The realization of the signals described above may change both at runtime and due to changes in implementation. You may be able to detect such changes using receive tracing or by inspecting message queues. However, these are internal implementation details of the runtime system that you should not rely on. As an example, many of the reply signals above are ordinary message signals. When the operation is synchronous, the reply signals do not have to be message signals. The current implementation takes advantage of this and, depending on the state of the system, use alternative ways of delivering the reply signals. The implementation of these reply signals may also, at any time, be changed to not use message signals where it previously did.

Receiving Signals

Signals are received asynchronously and automatically. There is nothing a process must do to handle the reception of signals, or can do to prevent it. In particular, signal reception is not tied to the execution of a receive expression, but can happen anywhere in the execution flow of a process.

When a signal is received by a process, some kind of action is taken. The specific action taken depends on the signal type, contents of the signal, and the state of the receiving process. Actions taken for the most common signals:

message
If the message signal was sent using a process alias that is no longer active, the message signal will be dropped; otherwise, if the alias is still active or the message signal was sent by other means, the message is added to the end of the message queue. When the message has been added to the message queue, the receiving process can fetch the message from the message queue using the receive expression.
link, unlink
Very simplified it can be viewed as updating process local information about the link. A detailed description of the link protocol can be found in the Distribution Protocol chapter of the ERTS User's Guide.
exit
Set the receiver in an exiting state, drop the signal, or convert the signal into a message and add it to the end of the message queue. If the receiver is set in an exiting state, no more Erlang code will be executed and the process is scheduled for termination. The section Receiving Exit Signals below gives more details on the action taken when an exit signal is received.
monitor, demonitor
Update process local information about the monitor.
down, change
Convert into a message if the corresponding monitor is still active; otherwise, drop the signal. If the signal is converted into a message, it is also added to the end of the message queue.
group_leader
Change the group leader of the process.
spawn_reply
Convert into a message, or drop the signal depending on the reply and how the spawn_request signal was configured. If the signal is converted into a message it is also added to the end of the message queue. For more information see the spawn_request() BIF.
alive_request
Schedule execution of the is alive test. If the process is in an exiting state, the is alive test will not be executed until after all directly visible Erlang resources used by the process have been released. The alive_reply will be sent after the is alive test has executed.
process_info_request, garbage_collect_request, check_process_code_request
Schedule execution of the requested operation. The reply signal will be sent when the operation has been executed.

Note that some actions taken when a signal is received involves scheduling further actions which will result in a reply signal when these scheduled actions have completed. This implies that the reply signals may be sent in a different order than the order of the incoming signals that triggered these operations. This does, however, not violate the signal ordering guarantee of the language.

The order of messages in the message queue of a process reflects the order in which the signals corresponding to the messages has been received since all signals that add messages to the message queue add them at the end of the message queue. Messages corresponding to signals from the same sender are also ordered in the same order as the signals were sent due to the signal ordering guarantee of the language.

Directly Visible Erlang Resources

As described above, exit signals due to links, down signals, and reply signals from an exiting process due to alive_requests are not sent until all directly visible Erlang resources held by the terminating process have been released. With directly visible Erlang resources we here mean all resources made available by the language excluding resources held by heap data, dirty native code execution and the process identifier of the terminating process. Examples of directly visible Erlang resources are registered name and ETS tables.

The Excluded Resources

The process identifier of the process cannot be released for reuse until everything regarding the process has been released.

A process executing dirty native code in a NIF when it receives an exit signal will be set into an exiting state even if it is still executing dirty native code. Directly visible Erlang resources will be released, but the runtime system cannot force the native code to stop executing. The runtime system tries to prevent the execution of the dirty native code from effecting other processes by, for example, disabling functionality such as enif_send() when used from a terminated process, but if the NIF is not well behaved it can still effect other processes. A well behaved dirty NIF should test if the process it is executing in has exited, and if so stop executing.

In the general case, the heap of a process cannot be removed before all signals that it needs to send have been sent. Resources held by heap data are the memory blocks containing the heap, but also include things referred to from the heap such as off heap binaries, and resources held via NIF resource objects on the heap.

Delivery of Signals

The amount of time that passes between the time a signal is sent and the arrival of the signal at the destination is unspecified but positive. If the receiver has terminated, the signal does not arrive, but it can trigger another signal. For example, a link signal sent to a non-existing process triggers an exit signal, which is sent back to where the link signal originated from. When communicating over the distribution, signals can be lost if the distribution channel goes down.

The only signal ordering guarantee given is the following: if an entity sends multiple signals to the same destination entity, the order is preserved; that is, if A sends a signal S1 to B, and later sends signal S2 to B, S1 is guaranteed not to arrive after S2. Note that S1 may, or may not have been lost.

Irregularities

Synchronous Error Checking

Some functionality that send signals have synchronous error checking when sending locally on a node and fail if the receiver is not present at the time when the signal is sent:

Unexpected Behaviours of Exit Signals

When a process sends an exit signal with exit reason normal to itself by calling erlang:exit(self(), normal) it will be terminated when theexitsignal is received. In all other cases when an exit signal with exit reason normal is received, it is dropped.

When an exitsignal with exit reasonkillis received, the action taken is different depending on whether the signal was sent due to a linked process terminating, or the signal was explicitly sent using the exit/2 BIF. When sent using the exit/2 BIF, the signal cannot be trapped, while it can be trapped if the signal was sent due to a link.

Blocking Signaling Over Distribution

When sending a signal over a distribution channel, the sending process may be suspended even though the signal is supposed to be sent asynchronously. This is due to the built in flow control over the channel that has been present more or less for ever. When the size of the output buffer for the channel reach the distribution buffer busy limit, processes sending on the channel will be suspended until the size of the buffer shrinks below the limit. The size of the limit can be inspected by calling erlang:system_info(dist_buf_busy_limit). Since this functionality has been present for so long, it is not possible to remove it, but it is possible to increase the limit to a point where it more or less never is reached using the erl command line argument +zdbbl. Note that if you do raise the limit like this, you need to take care of flow control yourself to ensure that you do not get into a situation with excessive memory usage.

The irregularities mentioned above cannot be fixed as they have been part of Erlang too long and it would break a lot of existing code.

13.6  Links

Two processes can be linked to each other. A link between two processes Pid1 and Pid2 is created by Pid1 calling the BIF link(Pid2) (or conversely). There also exist a number of spawn_link BIFs, which spawn and link to a process in one operation.

Links are bidirectional and there can only be one link between two processes. Repeated calls to link(Pid) have no effect.

A link can be removed by calling the BIF unlink(Pid).

Links are used to monitor the behaviour of other processes, see Error Handling.

13.7  Error Handling

Erlang has a built-in feature for error handling between processes. Terminating processes emit exit signals to all linked processes, which can terminate as well or handle the exit in some way. This feature can be used to build hierarchical program structures where some processes are supervising other processes, for example, restarting them if they terminate abnormally.

See OTP Design Principles for more information about OTP supervision trees, which use this feature.

Emitting Exit Signals

When a process terminates, it terminates with an exit reason as explained in Process Termination. This exit reason is emitted in an exit signal to all linked processes.

A process can also call the function exit(Pid,Reason). This results in an exit signal with exit reason Reason being emitted to Pid, but does not affect the calling process.

Receiving Exit Signals

The default behaviour when a process receives an exit signal with an exit reason other than normal, is to terminate and in turn emit exit signals with the same exit reason to its linked processes. An exit signal with reason normal is ignored.

A process can be set to trap exit signals by calling:

process_flag(trap_exit, true)

When a process is trapping exits, it does not terminate when an exit signal is received. Instead, the signal is transformed into a message {'EXIT',FromPid,Reason}, which is put into the mailbox of the process, just like a regular message.

An exception to the above is if the exit reason is kill, that is if exit(Pid,kill) has been called. This unconditionally terminates the process, regardless of if it is trapping exit signals.

13.8  Monitors

An alternative to links are monitors. A process Pid1 can create a monitor for Pid2 by calling the BIF erlang:monitor(process, Pid2). The function returns a reference Ref.

If Pid2 terminates with exit reason Reason, a 'DOWN' message is sent to Pid1:

{'DOWN', Ref, process, Pid2, Reason}

If Pid2 does not exist, the 'DOWN' message is sent immediately with Reason set to noproc.

Monitors are unidirectional. Repeated calls to erlang:monitor(process, Pid) creates several independent monitors, and each one sends a 'DOWN' message when Pid terminates.

A monitor can be removed by calling erlang:demonitor(Ref).

Monitors can be created for processes with registered names, also at other nodes.

13.9  Process Dictionary

Each process has its own process dictionary, accessed by calling the following BIFs:

put(Key, Value)
get(Key)
get()
get_keys(Value)
erase(Key)
erase()