This module provides a standard way of writing Client-Server applications. All servers written as generic servers share a common set of interface functions. The generic parts of the server contain functions for debugging, handling the termination of the parent, and presentation of illustrative error information if something goes wrong with the server.
The idea is that the implementation specific parts of a
client-server is in one module, called the callback module. The
callback module contains the client interface functions which use
the server access functions described below. The callback module
also contains the server callback functions, for example
handle_call/3
. Whenever the generic part of the server
receives a message - sent through a server access function, for
example - the corresponding callback function is called.
The relationship between the generic interface functions (and received messages) and the callback functions can be illustrated as follows:
Callback module gen_server ---------------- ---------- gen_server:start -----> start a new server Module:init/1 <----- looping gen_server:call -----> Module:handle_call/3 <----- gen_server:cast -----> Module:handle_cast/2 <----- gen_server:multi_call -----> Module:handle_call/3 <----- gen_server:abcast -----> Module:handle_cast/2 <----- Module:handle_info/2 <----- other message received. Module:terminate/2 <----- clean up before termination.
If the server wants to trap exit signals, this must be explicitly initiated in the callback module.
An instance of the gen_server
behaviour can be debugged
using the module sys
.
start(Module, Args, Options) -> ServerRet
start(ServerName, Module, Args, Options) ->
ServerRet
start_link(Module, Args, Options) ->
ServerRet
start_link(ServerName, Module, Args,
Options) -> ServerRet
Module = atom()
ServerName = {local, atom()} | {global, atom()}
Args = term()
Options = [Opt]
Opt = {debug, [Dbg]} | {timeout, Time}
Dbg = trace | log | statistics | {log_to_file, FileName} |
{install, {Func, FuncState}}
ServerRet = {ok, Pid} | ignore | {error, Reason}
Pid = pid()
Reason = {already_started, Pid} | term()
Starts a new server. If the server is started without
ServerName
, it can only be called using the returned
Pid
identifier. If started with ServerName
,
the name is registered locally or globally.
Module
is the name of the callback module.
A server started with start/3
or start/4
does
not care about the parent, which means that the parent is
not handled explicitly in the generic process part. If
started in this manner, these functions must not be
used if the server is a worker in a supervision tree.
A server started with start_link/3
or
start_link/4
is initially linked to the caller, the
parent, and it will terminate whenever the parent process
terminates, and with the same reason as the parent. If the
server traps exits, the terminate/2
callback function
is called in order to clean up before the termination. If
started in this manner, these functions should be
used if the server is a worker in a supervision tree.
Time
specifies how long time, in milliseconds, the
server is allowed to spend initializing.
The function Module:init(Args)
is called in the new
process in order to initialize the server (see below).
Refer to the sys
module for more information about
the Dbg
options.
call(ServerRef, Request) -> Reply
call(ServerRef, Request, Timeout) -> Reply
ServerRef = Name | {Name, Node} | {global, Name} | Pid
Name = atom()
Node = atom()
Request = term()
Timeout = int() > 0 | infinity
Reply = term()
A request is sent to the ServerRef
server. The
request can be any term, but the term must be recognized by
the server. The request is handled by the server (in the
Module:handle_call/2
function) and the client is
suspended while waiting for the response. Timeout
should be set to some reasonable value in milliseconds. The
special value infinity
can be used if the user has no
idea how long the request is supposed to take. The default
value is 5000
if Timeout
is not specified.
If Timeout
has an integer value and no response has
been delivered within Timeout
milliseconds, then the
client terminates with reason {timeout, {gen_server,
call, [ServerRef, Request, Timeout]}}
.
If the server should crash during the
request and the client is linked to the server and the
client is trapping exits, (phew) the exit message
is read out from the clients receive queue and then
this function call fails with the exit reason that was read.
This is a remnant from when monitors did not exist and
links was the only way to supervise the request, and the
behaviour may change in a future release. In this release,
unfortuneately, under certain circumstances
(e.g. ServerRef = {Name, Node}
,
Node
crashes during call) the exit message cannot
be read out. Note that if the server crashes in between calls,
the client must take care of the exit message anyway.
cast(ServerRef, Request) -> ok
ServerRef = Name | {Name, Node} | {global, Name} | Pid
Name = atom()
Node = atom()
Request = term()
A request is sent to the server. As no response will be
delivered, the client making the cast is not suspended until
the request has been handled by the server. This function
returns ok
immediately and ignores non-existing
servers.
multi_call(DistRef, Request) -> DistRep
multi_call(Nodes, DistRef, Request) -> DistRep
multi_call(Nodes, DistRef, Request, Timeout) -> DistRep
Nodes = [Node]
Node = atom()
DistRef = atom()
DistRep = {[{Node,Reply}],[Node]}
Request = term()
Timeout = int() >= 0 | infinity
Reply = term()
Sends a request to the locally registered server
DistRef
at every known node (or Nodes
). This
function returns a list of replies which are tagged with the
corresponding node name, and a list of bad
nodes. Reply
is the value returned by a server. A
node is marked bad if the server at a specific node, or the
node itself, does not exist.
The request is sent to the DistRef
server at all
nodes before the replies are collected. This ensures that
the request is handled in parallel on all nodes.
If one of the nodes is of an older Erlang release, and
its server is not started when the requests are sent, but
starts within 2 s after, this function waits the whole
This problem does not exist if all nodes are of the current release. |
If Timeout
is given, each node not replying within
that time is regarded as bad.
This function does not read out any exit messages
like call/2,3
does.
The previously undocumented functions
safe_multi_call/2..4
have now been removed
since multi_call/2..4
is now safe, except for
against old nodes as mentioned in the warning above.
abcast(DistRef, Request) -> abcast
abcast(Nodes, DistRef, Request) -> abcast
Nodes = [Node]
Node = atom()
DistRef = atom()
Request = term()
Broadcasts the request asynchronously to the locally
registered server DistRef
on every known node (or
Nodes
). This function returns immediately and ignores
non-existing servers or nodes.
To = {pid(), Tag}
Tag = term()
This function can be used by a server to make an explicit
reply, if a reply cannot be returned immediately as the
return value of Module:handle_call/3
. To
has
the same value as the From
argument in
Module:handle_call/3
.
The following functions
should be exported from a gen_server
callback module.
Module:init(Args) -> {ok, State} | {ok, State, Timeout} |
ignore | {stop, StopReason}
Args = term()
State = term()
Timeout = int() >= 0 | infinity
StopReason = term()
Whenever a new server is started, init/1
is the
first function called in the specified callback module. To
ensure a synchronized start-up procedure, the
gen_server:start
function will not return before the
init/1
function has returned.
The Args
argument supplied to the init/1
function is the same as the Args
parameter supplied
to the gen_server:start
functions.
The purpose of
the init/1
function is to initialize the server and
the internal state of the server. A server which holds an
external resource typically opens the associated port and
keeps the port identity in the internal state.
If the
server wants to trap exits, this has to be expressed
explicitly in the init function with
process_flag(trap_exit, true)
.
The representation
of the server State
is an implementation specific
detail which must be decided by the designer of the server.
State
will be visible as an argument to all callback
functions. To change something in State
, a new value
is returned from the callback function using the return
values (terms) described below.
If the initializing
procedure fails, the reason is supplied as StopReason
with the {stop, StopReason}
return value.
After
the server has been successfully initialized, the generic
part of the server enters the main loop and waits for
requests. A Timeout
time can be specified if the
server is only allowed to wait for a certain time for the
next event. If the timeout time elapses, the special
timeout
message should be handled in the
Module:handle_info/2
callback
function. Timeout
is specified in milliseconds.
This function can return ignore
in order to inform
the parent, especially if it is a supervisor, that the
server, as an example, did not start in accordance with the
configuration data.
Module:handle_call(Request, From, State) -> CallReply
Request = term()
From = {pid(), Tag}
Tag =
term()
CallReply = {reply, Reply, State} | {reply,
Reply, State, Timeout} | {noreply, State} | {noreply, State,
Timeout} | {stop, StopReason, Reply, State} | {stop,
StopReason, State}
Timeout = int() >= 0 | infinity
StopReason = normal | shutdown | term()
Whenever a client function has called one of the
interface functions gen_server:call
or
gen_server:multi_call
, the server handles the request
in this callback function.
Request
is the same as
the term supplied with the above client call. The server
decides if the client should be sent a reply directly
({reply, ...}
), indirectly ({noreply, ...}
),
or if the server has to terminate ({stop, ...}
) as a
result of the request. If {noreply, ...}
is returned,
a reply is sent to the client using the reply/2
function.
If StopReason
is something other than normal
or shutdown
, the server is assumed to have terminated
with a runtime error. In this case, a lot of information is
reported about the failure. The atom normal
causes a
normal termination of the server, while shutdown
causes an abnormal, but faultless, termination.
If the server decided to terminate {stop, StopReason [,
...]}
, the Module:terminate/2
function is
called. All code which handles the clean up before the
server terminates should be located in the terminate
function. The server will terminate due to
StopReason
.
As described above ( see init/1
), a timeout can be
specified to take some specific action if no more requests
are received within Timeout
milliseconds.
Module:handle_cast(Request, State) -> Return
Request = term()
State = term()
Return = {noreply, State} | {noreply, State, Timeout} |
{stop, StopReason, State}
Timeout = int() >= 0 | infinity
StopReason = normal | shutdown | term()
Whenever a client function has called one of the
interface functions gen_server:cast
or
gen_server:abcast
, the server handles the request in
this callback function. No reply will ever be sent to the
client, but the server can decide to
terminate. StopReason
is as described for
handle_call/3
.
Module:handle_info(Info, State) -> Return
Info = term()
State = term()
Return =
{noreply, State} | {noreply, State, Timeout} | {stop,
StopReason, State}
Timeout = int() >= 0 | infinity
StopReason = normal | shutdown | term()
This callback function handles received messages other
than call
and cast
. Typical messages which are
handled by this function include:
{'EXIT', Pid, Reason}
{nodedown, Node}
nodedown
message is handled here.
timeout
Timeout
milliseconds has elapsed since the last handled event, this
message should be handled.
Msg
Server ! Msg
are also
handled here.
Communication with the server should always go through the interface functions described above. |
The Return
value is the same as for handle_cast/2
.
StopReason
is as described for handle_call/3
.
Module:terminate(Reason, State) -> ok
Reason = term()
State = term()
This callback function is called whenever the server is
about to terminate. Either one of the above callback
functions have returned {stop, StopReason, ...}
, in
which case Reason
is equal to StopReason
; or
some other fault has been caught. Reason
is any term
which describes the termination reason. If the server traps
exits, the terminate
function is called if the
server's parent (normally a supervisor) dies or orders the
server to die. If the server does not trap exits, it dies
immediately if the parent dies.
With this function, the server can clean up before the process terminates. It can, for example, de-allocate external resources.
The termination reason cannot be
changed here. The server will terminate due toReason
regardless of what was returned from this function.
Module:code_change(OldVsn, State, Extra) -> {ok,
NewState}
OldVsn = undefined | term()
State = term()
Extra = term()
NewState = term()
This function is called when a code change is performed,
which implies that the internal data structures of the
server has changed. This function is supposed to convert the
old state to the new one. OldVsn
is the vsn
attribute of the old version of the module. If no such
attribute was defined, the atom undefined
is sent.
Extra
is an optional term which is typically defined
in the release upgrade script.
The gen_server
behaviour
generates the following system events, handled by the sys
module:
{in, Msg}
when a message is received.
{out, Msg, To, State}
when a message is sent.
{noreply, State}
when no reply is delivered.
The following example implements a simple queue server. The server has four interface functions:
start/0
which starts the queue server.
stop/0
which stops the queue server.
in/1
which inserts an item last in the queue.
out/0
which removes the oldest item from the queue.
The queue server is not linked to the parent process and the server does not handle the termination of the parent process explicitly.
-module(queue_serv). -behaviour(gen_server). %% External exports -export([start/0, in/1, out/0, stop/0]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]). start() -> gen_server:start({local, queue_serv}, queue_serv, [], []). in(Item) -> gen_server:call(queue_serv, {in, Item}). out() -> gen_server:call(queue_serv, out). stop() -> gen_server:call(queue_serv, stop). %% Callback functions. init([]) -> {ok, {[],[]}}. handle_call({in, X}, _From, {In, Out}) -> {reply, ok, {[X|In], Out}}; handle_call(out, _From, Queue) -> {Reply, NewQueue} = out(Queue), {reply, Reply, NewQueue}; handle_call(stop, _From, Queue) -> {stop, normal, ok, Queue}. handle_cast(_, State) -> {noreply, State}. handle_info(_, State) -> {noreply, State}. terminate(Reason, State) -> ok. %% Internal functions out({In, [H|Out]}) -> {{value, H}, {In, Out}}; out({[], []}) -> {empty, {[],[]}}; out({In, _}) -> out({[], lists:reverse(In)}).
sys(3)