This chapter should be read in conjunction with
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.
An example of a simple server written in plain Erlang was
given in
-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.
In the example in the previous section, the gen_server is started
by calling
start_link() ->
gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}
The first argument
If the name is omitted, the gen_server is not registered.
Instead its pid must be used. The name could also be given
as
The second argument,
In this case, the interface functions (
The third argument, [], is a term which is passed as-is to
the callback function
The fourth argument, [], is a list of options. See
If name registration succeeds, the new gen_server process calls
the callback function
init(_Args) ->
{ok, channels()}.
Note that
The synchronous request
alloc() ->
gen_server:call(ch3, alloc).
The request is made into a message and sent to the gen_server.
When the request is received, the gen_server calls
handle_call(alloc, _From, Chs) ->
{Ch, Chs2} = alloc(Chs),
{reply, Ch, Chs2}.
In this case, the reply is the allocated channel
Thus, the call
The asynchronous request
free(Ch) ->
gen_server:cast(ch3, {free, Ch}).
The request is made into a message and sent to the gen_server.
When the request is received, the gen_server calls
handle_cast({free, Ch}, Chs) ->
Chs2 = free(Ch, Chs),
{noreply, Chs2}.
In this case, the new state is the updated list of available
channels
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
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(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, 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
If the gen_server should be able to receive other messages than
requests, the callback function
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}.