Erlang -- Gen_Server Behaviour

User's Guide
PDF
Top

OTP Design Principles
User's Guide
Version 5.9.2

Expand All
Contract All

Chapters

- Overview
  - Top of chapter
  - Supervision Trees
  - Behaviours
  - Applications
  - Releases
  - Release Handling
- Gen_Server Behaviour
  - Top of chapter
  - Client-Server Principles
  - Example
  - Starting a Gen_Server
  - Synchronous Requests - Call
  - Asynchronous Requests - Cast
  - Stopping
  - Handling Other Messages
- Gen_Fsm Behaviour
  - Top of chapter
  - Finite State Machines
  - Example
  - Starting a Gen_Fsm
  - Notifying About Events
  - Timeouts
  - All State Events
  - Stopping
  - Handling Other Messages
- Gen_Event Behaviour
  - Top of chapter
  - Event Handling Principles
  - Example
  - Starting an Event Manager
  - Adding an Event Handler
  - Notifying About Events
  - Deleting an Event Handler
  - Stopping
  - Handling Other Messages
- Supervisor Behaviour
  - Top of chapter
  - Supervision Principles
  - Example
  - Restart Strategy
  - Maximum Restart Frequency
  - Child Specification
  - Starting a Supervisor
  - Adding a Child Process
  - Stopping a Child Process
  - Simple-One-For-One Supervisors
  - Stopping
- Sys and Proc_Lib
  - Top of chapter
  - Simple Debugging
  - Special Processes
  - User-Defined Behaviours
- Applications
  - Top of chapter
  - Application Concept
  - Application Callback Module
  - Application Resource File
  - Directory Structure
  - Application Controller
  - Loading and Unloading Applications
  - Starting and Stopping Applications
  - Configuring an Application
  - Application Start Types
- Included Applications
  - Top of chapter
  - Definition
  - Specifying Included Applications
  - Synchronizing Processes During Startup
- Distributed Applications
  - Top of chapter
  - Definition
  - Specifying Distributed Applications
  - Starting and Stopping Distributed Applications
  - Failover
  - Takeover
- Releases
  - Top of chapter
  - Release Concept
  - Release Resource File
  - Generating Boot Scripts
  - Creating a Release Package
  - Directory Structure
- Release Handling
  - Top of chapter
  - Release Handling Principles
  - Requirements
  - Distributed Systems
  - Release Handling Instructions
  - Application Upgrade File
  - Release Upgrade File
  - Installing a Release
  - Updating Application Specifications
- Appup Cookbook
  - Top of chapter
  - Changing a Functional Module
  - Changing a Residence Module
  - Changing a Callback Module
  - Changing Internal State
  - Module Dependencies
  - Changing Code For a Special Process
  - Changing a Supervisor
  - Adding or Deleting a Module
  - Starting or Terminating a Process
  - Adding or Removing an Application
  - Restarting an Application
  - Changing an Application Specification
  - Changing Application Configuration
  - Changing Included Applications
  - Changing Non-Erlang Code
  - Emulator Restart and Upgrade
  - Emulator Upgrade from pre OTP R15

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

Figure 2.1: 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.

gen_server:start_link must be used if the gen_server is part of a supervision tree, i.e. is started by a supervisor. There is another function gen_server:start to start a stand-alone gen_server, i.e. a gen_server which is not part of a supervision tree.

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

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

### 2.6 Stopping

#### In a Supervision Tree

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. 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 timeout value and the gen_server must be set to trap exit signals in the init function. When ordered to shutdown, the gen_server will then call the callback function terminate(shutdown, State):

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

    ...

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

#### Stand-Alone Gen_Servers

If the gen_server is not part of a supervision tree, a stop function may 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 will cause the gen_server to call terminate(normal,State1) and then terminate gracefully.

### 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 of other messages are 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, State1}.

The code_change method also has to be implemented.

    code_change(OldVsn, State, Extra) ->
        ..code to convert state (and more) during code change
        {ok, NewState}.