> ## 2 Gen_Server Behaviour

> This chapter should be read in conjunction with `gen_server(3)`, where all interface functions and callback functions are described in detail.

> ### 2.1 Client-Server Principles

> The client-server model is characterized by a central server and an arbitrary number of clients. The client-server model is generally used for resource management operations, where several different clients want to share a common resource. The server is responsible for managing this resource.

> Client-Server Model

> ### 2.2 Example

> An example of a simple server written in plain Erlang was given in Overview. The server can be re-implemented 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.

> ### 2.3 Starting a Gen_Server

> In the example in the previous section, the gen_server is started by calling `ch3:start_link()`:

>     start_link() ->
>         gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}

> `start_link` calls the 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. In this case, the gen_server will be locally registered as `ch3`.
>   If the name is omitted, the gen_server is not registered. Instead its pid must be used. The name could also be given as `{global, Name}`, in which case the gen_server is registered using `global:register_name/2`.
> - The second argument, `ch3`, is the name of the callback module, that is the module where the callback functions are located.
>   In this case, the interface functions (`start_link`, `alloc` and `free`) are located in the same module as the callback functions (`init`, `handle_call` and `handle_cast`). This is normally good programming practice, to have the code corresponding to one process contained in one module.
> - The third argument, [], is a term which is passed as-is to the callback function `init`. Here, `init` does not need any indata and ignores the argument.
> - The fourth argument, [], is a list of options. See `gen_server(3)` for 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()}.

> Note that `gen_server:start_link` is synchronous. It does not return until the gen_server has been initialized and is ready to receive requests.

> ### 2.4 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 which should 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 remaining available channels `Chs2`.

> Thus, the call `ch3:alloc()` will return the allocated channel `Ch` and the gen_server then waits for new requests, now with an updated list of available channels.

> ### 2.5 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 server.

>     handle_call({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.

> ### 2.6 Stopping

> If the gen_server is part of a supervision tree, no stop function is needed. The gen_server will automatically be terminated by its supervisor calling `exit(Pid, shutdown)`.

> The gen_server process does not trap exit signals. If it is necessary to do some cleaning up before termination, it should be set to trap exit signals in the `init` function and another callback function `terminate(Reason, State)` should be implemented doing the cleaning up:

>     init(Args) ->
>         ...,
>         process_flag(trap_exit, true),
>         ...,
>         {ok, State}.

>     ...

>     terminate(shutdown, State) ->
>         ..code for cleaning up here..
>         ok.

> If the gen_server is not part of a supervision tree, a stop function may be useful. The gen_server will automatically call the callback function `terminate(Reason, State)` if another of the callback functions returns `{stop, Reason, State2}` instead of `{reply,...}` or `{noreply,...}`. 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.

> ### 2.7 Handling Other Messages

> If the gen_server should be able to receive other messages than requests, the callback function `handle_info(Info, State)` must be implemented to handle them. Examples on other messages could be for example timeouts or exit messages, if the gen_server is linked to other processes (than the supervisor) and trapping exit signals.

>     handle_info({'EXIT', Pid, Reason}, State) ->
>         ..code to handle exits here..
>         {noreply, State}.

Copyright © 1991-2003 Ericsson Utvecklings AB