Elixir v1.3.4 GenServer behaviour
A behaviour module for implementing the server of a client-server relation.
A GenServer is a process like any other Elixir process and it can be used to keep state, execute code asynchronously and so on. The advantage of using a generic server process (GenServer) implemented using this module is that it will have a standard set of interface functions and include functionality for tracing and error reporting. It will also fit into a supervision tree.
Example
The GenServer behaviour abstracts the common client-server interaction. Developers are only required to implement the callbacks and functionality they are interested in.
Let’s start with a code example and then explore the available callbacks. Imagine we want a GenServer that works like a stack, allowing us to push and pop items:
We start our
Stack by calling
start_link/3, passing the module
with the server implementation and its initial argument (a list
representing the stack containing the item
:hello). We can primarily
interact with the server by sending two types of messages. call
messages expect a reply from the server (and are therefore synchronous)
while cast messages do not.
Every time you do a
GenServer.call/3, the client will send a message
that must be handled by the
handle_call/3 callback in the GenServer.
A
cast/2 message must be handled by
handle_cast/2. Registration
Both
start_link/3 and
start/3 support the
GenServer to register
a name on start via the
:name option. Registered names are also
automatically cleaned up on termination. The supported values are:
an atom - the GenServer is registered locally with the given name using
Process.register/2.
{:global, term}- the GenServer is registered globally with the given term using the functions in the
:globalmodule.
{:via, module, term}- the GenServer is registered with the given mechanism and name. The
:viaoption expects a module that exports
register_name/2,
unregister_name/1,
whereis_name/1and
send/2. One such example is the
:globalmodule which uses these functions for keeping the list of names of processes and their associated pid’s that are available globally for a network of Erlang nodes.
For example, we could start and register our Stack server locally as follows:
# Start the server and register it locally with name MyStack {:ok, _} = GenServer.start_link(Stack, [:hello], name: MyStack) # Now messages can be sent directly to MyStack GenServer.call(MyStack, :pop) #=> :hello
Once the server is started, the remaining functions in this module (
call/3,
cast/2, and friends) will also accept an atom, or any
:global or
:via
tuples. In general, the following formats are supported:
- a
pid
- an
atomif the server is locally registered
{atom, node}if the server is locally registered at another node
{:global, term}if the server is globally registered
{:via, module, name}if the server is registered through an alternative registry
Client / Server APIs
Although in the example above we have used
GenServer.start_link/3 and
friends to directly start and communicate with the server, most of the
time we don’t call the
GenServer functions directly. Instead, we wrap
the calls in new functions representing the public API of the server.
Here is a better implementation of our Stack module:
defmodule Stack do use GenServer # Client def start_link(default).
Summary
Types
Return values of
start* functions
Functions
Casts all servers locally registered as
name at the specified nodes
Makes a synchronous call to the
server and waits for its reply
Sends an asynchronous request to the
server
Calls all servers locally registered as
name at the specified
nodes
Replies to a client
Stops the server with the given
reason
Returns the
pid or
{name, node} of a GenServer process, or options supported by the
start* functions
Tuple describing the client of a call request.
pid is the pid of the caller and
tag is a unique term used to identify the
call.
Return values of
start* functions
option :: {:debug, debug} | {:name, name} | {:timeout, timeout} | {:spawn_opt, Process.spawn_opt}
Option values used by the
start* functions
Functions
Casts all servers locally registered as
name at the specified nodes..
server.
Sends an asynchronous request to the
server.
This function always returns
:ok regardless of whether
the destination
server (or node) exists. Therefore it
is unknown whether the destination
server successfully
handled the message..
multi_call([node], name :: atom, term, timeout) :: {replies :: [{node, term}], bad_nodes :: [node]}
Calls all servers locally registered as
name at the specified
nodes.
First, the
request is sent to every node in
nodes; then, the caller waits
for the replies. This function returns a two-element tuple
{replies,
bad_nodes} where:
replies- is a list of
{node, reply}tuples where
nodeis the node that replied and
replyis its reply
bad_nodes- is a list of nodes that either did not exist or where a server with the given
namedid not exist or did not reply
nodes is a list of node names to which the request is sent. The default
value is the list of all known nodes (including this node).
To avoid that late answers (after the timeout) pollute the caller’s message queue, a middleman process is used to do the actual calls. Late answers will then be discarded when they arrive to a terminated process.
Examples
Assuming the
Stack GenServer mentioned in the docs for the
GenServer
module is registered as
Stack in the
:"foo@my-machine" and
:"bar@my-machine" nodes:
GenServer.multi_call(Stack, :pop) #=> {[{:"foo@my-machine", :hello}, {:"bar@my-machine", :world}], []}
Replies to a client.
This function can be used to explicitely send a reply to a client that called
call/3 or
multi_call GenServer
that originally received the call (as long as that GenServer communicated the
from argument somehow).
This function always returns
:ok.
Examples
def handle_call(:reply_in_one_second, from, state) do Process.send_after(self(), {:reply, from}, 1_000) {:noreply, state} end def handle_info({:reply, from}, state) do GenServer.reply(from, :one_second_has_passed) end, the
init/1 function of the given
module is
called with
args as its arguments to initialize the server. To ensure a
synchronized start-up procedure, this function does not return until
init/1
has returned.
Note that a
GenServer started with
start_link/3 is linked to the
parent process and will exit in case of crashes from the parent. The GenServer
will also exit due to the
:normal reasons in case it is configured to trap
exits in the
init/1 callback.
Options
:name- used for name registration as described in the “Name registration” section of the module documentation
:timeout- if present, the server is allowed to spend the given amount of milliseconds initializing or it will be terminated and the start function will return
{:error, :timeout}
:debug- if present, the corresponding function in the
:sysmodule is invoked
:spawn_opt- if present, its value is passed as options to the underlying process as in
Process.spawn/4
Return values
If the server is successfully created and initialized, this function returns
{:ok, pid}, where
pid is the pid of the server. If a process with the
specified server name already exists, this function returns
{:error, {:already_started, pid}} with the pid of that process.
If the
init/1 callback fails with
reason, this function returns
{:error, reason}. Otherwise, if it returns
{:stop, reason}
or
:ignore, the process is terminated and this function returns
{:error, reason} or
:ignore, respectively.
Stops the server with the given
reason.
The
terminate/2 callback of the given
server.
Returns the
pid or
{name, node} of a GenServer process, or
nil if
no process is associated with the given name.
Examples
For example, to lookup a server process, monitor it and send a cast to it:
process = GenServer.whereis(server) monitor = Process.monitor(process) GenServer.cast(process, :hello)
Callbacks
code_change(old_vsn, state :: term, extra :: term) :: {:ok, new_state :: term} | {:error, reason :: term} when old_vsn: term | {:down, term}
Invoked to change the state of the
GenServer when a different version of a
module is loaded (hot code swapping) and the state’s term structure should be
changed.
old_vsn is the previous version of the module (defined by the
@vsn
attribute) when upgrading. When downgrading the previous version is wrapped in
a 2-tuple with first element
:down.
state is the current state of the
GenServer and
extra is any extra data required to change the state.
Returning
{:ok, new_state} changes the state to
new_state and the code
change is successful.
Returning
{:error, reason} fails the code change with reason
reason and
the state remains as the previous state.
If
code_change/3 raises the code change fails and the loop will continue
with its previous state. Therefore this callback does not usually contain side effects.
format_status(reason, pdict_and_state :: list) :: term when reason: :normal | :terminate
Invoked in some cases to retrieve a formatted version of the
GenServer status.
This callback can be useful to control the appearance of the status of the
GenServer. For example, it can be used to return a compact representation of
the
GenServers’s state to avoid having large state terms printed.
one of
:sys.get_status/1or
:sys.get_status/2is invoked to get the status of the
GenServer; in such cases,
reasonis
:normal
- the
GenServerterminates abnormally and logs an error; in such cases,
reasonis
:terminate
pdict_and_state is a two-elements list
[pdict, state] where
pdict is a
list of
{key, value} tuples representing the current process dictionary of
the
GenServer and
state is the current state of the
GenServer.
handle_call(request :: term, from, state :: term) :: {:reply, reply, new_state} | {:reply, reply, new_state, timeout | :hibernate} | {:noreply, new_state} | {:noreply, new_state, timeout | :hibernate} | {:stop, reason, reply, new_state} | {:stop, reason, new_state} when reply: term, new_state: term, reason: term
Invoked to handle synchronous
call/3 messages.
call/3 will block until a
reply is received (unless the call times out or nodes are disconnected).
request is the request message sent by a
call/3,
from is a 2-tuple
containing the caller’s pid and a term that uniquely identifies the call, and
state is the current state of the
GenServer.
Returning
{:reply, reply, new_state} sends the response
reply to the
caller and continues the loop with new state
new_state.
Returning
{:reply, reply, new_state, timeout} is similar to
{:reply, reply, new_state} except
handle_info(:timeout, new_state) will be
called after
timeout milliseconds if no messages are received.
Returning
{:reply, reply, new_state, :hibernate} is similar to
{:reply, reply, new_state} except the process is hibernated and will
continue the loop once a message is in its message queue. If a message is
already in the message queue this will be immediately. Hibernating a
GenServer causes garbage collection and leaves a continuous heap that
minimises the memory used by the process.
Hibernating should not be used aggressively as too much time could be spent garbage collecting. Normally it should only be used when a message is not expected soon and minimising the memory of the process is shown to be beneficial.
Returning
{:noreply, new_state} does not send a response to the caller and
continues the loop with new state
new_state. The response must be sent with
reply/2.
There are three main use cases for not replying using the return value:
- To reply before returning from the callback because the response is known before calling a slow function.
- To reply after returning from the callback because the response is not yet available.
- To reply from another process, such as a task.
When replying from another process the
GenServer should exit if the other
process exits without replying as the caller will be blocking awaiting a
reply.
Returning
{:noreply, new_state, timeout | :hibernate} is similar to
{:noreply, new_state} except a timeout or hibernation occurs as with a
:reply tuple.
Returning
{:stop, reason, reply, new_state} stops the loop and
terminate/2
is called with reason
reason and state
new_state. Then the
reply is sent
as the response to call and the process exits with reason
reason.
Returning
{:stop, reason, new_state} is similar to
{:stop, reason, reply, new_state} except a reply is not sent.
If this callback is not implemented, the default implementation by
use GenServer will return
{:stop, {:bad_call, request}, state}.
handle_cast(request :: term, state :: term) :: {:noreply, new_state} | {:noreply, new_state, timeout | :hibernate} | {:stop, reason :: term, new_state} when new_state: term
Invoked to handle asynchronous
cast/2 messages.
request is the request message sent by a
cast/2 and
state is the current
state of the
GenServer.
Returning
{:noreply, new_state} continues the loop with new state
new_state.
Returning
{:noreply, new_state, timeout} is similar to
{:noreply,.
If this callback is not implemented, the default implementation by
use GenServer will return
{:stop, {:bad_cast, request}, state}..
If this callback is not implemented, the default implementation by
use GenServer will return
{:noreply, state}..
Returning
:ignore will cause
start_link/3 to return
:ignore and the
process will exit normally without entering the loop or calling
terminate/2.
If used when part of a supervision tree the parent supervisor will not fail
to start nor immediately try to restart the
GenServer. The remainder of the
supervision tree will be (re)started and so the
GenServer should not be
required by other processes. It can be started later with
Supervisor.restart_child/2 as the child specification is saved in the parent
supervisor. The main use cases for this are:
- The
GenServeris disabled by configuration but might be enabled later.
- An error occurred and it will be handled by a different mechanism than the
Supervisor. Likely this approach involves calling
Supervisor.restart_child/2after a delay to attempt a restart.
Returning
{:stop, reason} will cause
start_link/3 to return
{:error, reason} and the process to exit with reason
reason without
entering the loop or calling
terminate/2.
terminate(reason, state :: term) :: term when reason: :normal | :shutdown | {:shutdown, term} | term
Invoked when the server is about to exit. It should do any cleanup required.
reason is exit reason and
state is the current state of the
GenServer.
The return value is ignored.
terminate/2 is called if.
If the
GenServer receives an exit signal (that is not
:normal) from any
process when it is not trapping exits it will exit abruptly with the same
reason and so not call
terminate/2. Note that a process does NOT trap
exits by default and an exit signal is sent when a linked process exits or its
node is disconnected.
Therefore it is not guaranteed that
terminate/2 is called when a
GenServer
exits. For such reasons, we usually recommend important clean-up rules to
happen in separated processes either by use of monitoring or by links
themselves..