Search documentation of Erlang System Documentation

# gen_server Behaviour

Copy Markdown View Source

It 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;
        end

## Example

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_server` is not registered. Instead its pid must be used. The name can 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, 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, `init` does not need any indata and ignores the argument.

- The fourth argument, `[]`, is a list of options. See `gen_server` for 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}.

← Previous Page Overview
Next Page → gen_statem Behaviour