gen_server Behaviour
View SourceIt is recommended to read this section alongside gen_server in STDLIB.
Client-Server Principles
The client-server model is characterized by a central server and an arbitrary number of clients. The client-server model is used for resource management operations, where several different clients want to share a common resource. The server is responsible for managing this resource.
---
title: Client Server Model
---
flowchart LR
    client1((Client))
    client2((Client))
    client3((Client))
    server((Server))
    client1 --> server
    server -.-> client1
    client2 --> server
    server -.-> client2
    client3 --> server
    server -.-> client3
    subgraph Legend
        direction LR
        start1[ ] -->|Query| stop1[ ]
        style start1 height:0px;
        style stop1 height:0px;
        start2[ ] -.->|Reply| stop2[ ]
        style start2 height:0px;
        style stop2 height:0px;
    endExample
An example of a simple server written in plain Erlang is provided in
Overview. The server can be reimplemented using
gen_server, resulting in this callback module:
-module(ch3).
-behaviour(gen_server).
-export([start_link/0]).
-export([alloc/0, free/1]).
-export([init/1, handle_call/3, handle_cast/2]).
start_link() ->
    gen_server:start_link({local, ch3}, ch3, [], []).
alloc() ->
    gen_server:call(ch3, alloc).
free(Ch) ->
    gen_server:cast(ch3, {free, Ch}).
init(_Args) ->
    {ok, channels()}.
handle_call(alloc, _From, Chs) ->
    {Ch, Chs2} = alloc(Chs),
    {reply, Ch, Chs2}.
handle_cast({free, Ch}, Chs) ->
    Chs2 = free(Ch, Chs),
    {noreply, Chs2}.The code is explained in the next sections.
Starting a Gen_Server
In the example in the previous section, gen_server is started by calling
ch3:start_link():
start_link() ->
    gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}start_link/0 calls function gen_server:start_link/4.  This function
spawns and links to a new process, a gen_server.
- The first argument, - {local, ch3}, specifies the name. The gen_server is then locally registered as- ch3.- If the name is omitted, the - gen_serveris not registered. Instead its pid must be used. The name can also be given as- {global, Name}, in which case the- gen_serveris registered using- global:register_name/2.
- The second argument, - ch3, is the name of the callback module, which is the module where the callback functions are located.- The interface functions ( - start_link/0,- alloc/0, and- free/1) are located in the same module as the callback functions (- init/1,- handle_call/3, and- handle_cast/2). It is usually good programming practice to have the code corresponding to one process contained in a single module.
- The third argument, - [], is a term that is passed as is to the callback function- init. Here,- initdoes not need any indata and ignores the argument.
- The fourth argument, - [], is a list of options. See- gen_serverfor the available options.
If name registration succeeds, the new gen_server process calls the callback
function ch3:init([]). init is expected to return {ok, State}, where
State is the internal state of the gen_server. In this case, the state is
the available channels.
init(_Args) ->
    {ok, channels()}.gen_server:start_link/4 is synchronous. It does not return until the
gen_server has been initialized and is ready to receive requests.
gen_server:start_link/4 must be used if the gen_server is part of
a supervision tree, meaning that it was started by a supervisor. There
is another function, gen_server:start/4, to start a standalone
gen_server that is not part of a supervision tree.
Synchronous Requests - Call
The synchronous request alloc() is implemented using gen_server:call/2:
alloc() ->
    gen_server:call(ch3, alloc).ch3 is the name of the gen_server and must agree with the name
used to start it. alloc is the actual request.
The request is made into a message and sent to the gen_server.
When the request is received, the gen_server calls
handle_call(Request, From, State), which is expected to return
a tuple {reply,Reply,State1}. Reply is the reply that is to be sent back
to the client, and State1 is a new value for the state of the gen_server.
handle_call(alloc, _From, Chs) ->
    {Ch, Chs2} = alloc(Chs),
    {reply, Ch, Chs2}.In this case, the reply is the allocated channel Ch and the new state is the
set of remaining available channels Chs2.
Thus, the call ch3:alloc() returns the allocated channel Ch and the
gen_server then waits for new requests, now with an updated list of
available channels.
Asynchronous Requests - Cast
The asynchronous request free(Ch) is implemented using gen_server:cast/2:
free(Ch) ->
    gen_server:cast(ch3, {free, Ch}).ch3 is the name of the gen_server. {free, Ch} is the actual request.
The request is made into a message and sent to the gen_server.
cast, and thus free, then returns ok.
When the request is received, the gen_server calls
handle_cast(Request, State), which is expected to return a tuple
{noreply,State1}. State1 is a new value for the state of the gen_server.
handle_cast({free, Ch}, Chs) ->
    Chs2 = free(Ch, Chs),
    {noreply, Chs2}.In this case, the new state is the updated list of available channels Chs2.
The gen_server is now ready for new requests.
Stopping
In a Supervision Tree
If the gen_server is part of a supervision tree, no stop function is needed.
The gen_server is automatically terminated by its supervisor.  Exactly how
this is done is defined by a shutdown strategy
set in the supervisor.
If it is necessary to clean up before termination, the shutdown strategy
must be a time-out value and the gen_server must be set to trap exit signals
in function init. When ordered to shutdown, the gen_server then calls
the callback function terminate(shutdown, State):
init(Args) ->
    ...,
    process_flag(trap_exit, true),
    ...,
    {ok, State}.
...
terminate(shutdown, State) ->
    %% Code for cleaning up here
    ...
    ok.Standalone Gen_Servers
If the gen_server is not part of a supervision tree, a stop function
can be useful, for example:
...
export([stop/0]).
...
stop() ->
    gen_server:cast(ch3, stop).
...
handle_cast(stop, State) ->
    {stop, normal, State};
handle_cast({free, Ch}, State) ->
    ...
...
terminate(normal, State) ->
    ok.The callback function handling the stop request returns a tuple
{stop,normal,State1}, where normal specifies that it is
a normal termination and State1 is a new value for the state
of the gen_server.  This causes the gen_server to call
terminate(normal, State1) and then it terminates gracefully.
Handling Other Messages
If the gen_server is to be able to receive other messages than requests,
the callback function handle_info(Info, State) must be implemented
to handle them.  Examples of other messages are exit messages,
if the gen_server is linked to other processes than the supervisor
and it is trapping exit signals.
handle_info({'EXIT', Pid, Reason}, State) ->
    %% Code to handle exits here.
    ...
    {noreply, State1}.The final function to implement is code_change/3:
code_change(OldVsn, State, Extra) ->
    %% Code to convert state (and more) during code change.
    ...
    {ok, NewState}.