aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc/design_principles/gen_server_concepts.xml
diff options
context:
space:
mode:
Diffstat (limited to 'system/doc/design_principles/gen_server_concepts.xml')
-rw-r--r--system/doc/design_principles/gen_server_concepts.xml269
1 files changed, 269 insertions, 0 deletions
diff --git a/system/doc/design_principles/gen_server_concepts.xml b/system/doc/design_principles/gen_server_concepts.xml
new file mode 100644
index 0000000000..8131c47a69
--- /dev/null
+++ b/system/doc/design_principles/gen_server_concepts.xml
@@ -0,0 +1,269 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Gen_Server Behaviour</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>gen_server_concepts.xml</file>
+ </header>
+ <marker id="gen_server"></marker>
+ <p>This chapter should be read in conjunction with
+ <seealso marker="stdlib:gen_server">gen_server(3)</seealso>,
+ where all interface functions and callback
+ functions are described in detail.</p>
+
+ <section>
+ <title>Client-Server Principles</title>
+ <p>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.</p>
+ <marker id="clientserver"></marker>
+ <image file="../design_principles/clientserver.gif">
+ <icaption>Client-Server Model</icaption>
+ </image>
+ </section>
+
+ <section>
+ <title>Example</title>
+ <p>An example of a simple server written in plain Erlang was
+ given in <seealso marker="des_princ#ch1">Overview</seealso>.
+ The server can be re-implemented using <c>gen_server</c>,
+ resulting in this callback module:</p>
+ <marker id="ex"></marker>
+ <code type="none">
+-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}.</code>
+ <p>The code is explained in the next sections.</p>
+ </section>
+
+ <section>
+ <title>Starting a Gen_Server</title>
+ <p>In the example in the previous section, the gen_server is started
+ by calling <c>ch3:start_link()</c>:</p>
+ <code type="none">
+start_link() ->
+ gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}</code>
+ <p><c>start_link</c> calls the function
+ <c>gen_server:start_link/4</c>. This function spawns and links to
+ a new process, a gen_server.</p>
+ <list type="bulleted">
+ <item>
+ <p>The first argument <c>{local, ch3}</c> specifies the name. In
+ this case, the gen_server will be locally registered as
+ <c>ch3</c>.</p>
+ <p>If the name is omitted, the gen_server is not registered.
+ Instead its pid must be used. The name could also be given
+ as <c>{global, Name}</c>, in which case the gen_server is
+ registered using <c>global:register_name/2</c>.</p>
+ </item>
+ <item>
+ <p>The second argument, <c>ch3</c>, is the name of the callback
+ module, that is the module where the callback functions are
+ located.</p>
+ <p>In this case, the interface functions (<c>start_link</c>,
+ <c>alloc</c> and <c>free</c>) are located in the same module
+ as the callback functions (<c>init</c>, <c>handle_call</c> and
+ <c>handle_cast</c>). This is normally good programming
+ practice, to have the code corresponding to one process
+ contained in one module.</p>
+ </item>
+ <item>
+ <p>The third argument, [], is a term which is passed as-is to
+ the callback function <c>init</c>. Here, <c>init</c> does not
+ need any indata and ignores the argument.</p>
+ </item>
+ <item>
+ <p>The fourth argument, [], is a list of options. See
+ <c>gen_server(3)</c> for available options.</p>
+ </item>
+ </list>
+ <p>If name registration succeeds, the new gen_server process calls
+ the callback function <c>ch3:init([])</c>. <c>init</c> is expected
+ to return <c>{ok, State}</c>, where <c>State</c> is the internal
+ state of the gen_server. In this case, the state is the available
+ channels.</p>
+ <code type="none">
+init(_Args) ->
+ {ok, channels()}.</code>
+ <p>Note that <c>gen_server:start_link</c> is synchronous. It does
+ not return until the gen_server has been initialized and is ready
+ to receive requests.</p>
+ <p><c>gen_server:start_link</c> must be used if the gen_server is
+ part of a supervision tree, i.e. is started by a supervisor.
+ There is another function <c>gen_server:start</c> to start a
+ stand-alone gen_server, i.e. a gen_server which is not part of a
+ supervision tree.</p>
+ </section>
+
+ <section>
+ <title>Synchronous Requests - Call</title>
+ <p>The synchronous request <c>alloc()</c> is implemented using
+ <c>gen_server:call/2</c>:</p>
+ <code type="none">
+alloc() ->
+ gen_server:call(ch3, alloc).</code>
+ <p><c>ch3</c> is the name of the gen_server and must agree with
+ the name used to start it. <c>alloc</c> is the actual request.</p>
+ <p>The request is made into a message and sent to the gen_server.
+ When the request is received, the gen_server calls
+ <c>handle_call(Request, From, State)</c> which is expected to
+ return a tuple <c>{reply, Reply, State1}</c>. <c>Reply</c> is
+ the reply which should be sent back to the client, and
+ <c>State1</c> is a new value for the state of the gen_server.</p>
+ <code type="none">
+handle_call(alloc, _From, Chs) ->
+ {Ch, Chs2} = alloc(Chs),
+ {reply, Ch, Chs2}.</code>
+ <p>In this case, the reply is the allocated channel <c>Ch</c> and
+ the new state is the set of remaining available channels
+ <c>Chs2</c>.</p>
+ <p>Thus, the call <c>ch3:alloc()</c> returns the allocated channel
+ <c>Ch</c> and the gen_server then waits for new requests, now
+ with an updated list of available channels.</p>
+ </section>
+
+ <section>
+ <title>Asynchronous Requests - Cast</title>
+ <p>The asynchronous request <c>free(Ch)</c> is implemented using
+ <c>gen_server:cast/2</c>:</p>
+ <code type="none">
+free(Ch) ->
+ gen_server:cast(ch3, {free, Ch}).</code>
+ <p><c>ch3</c> is the name of the gen_server. <c>{free, Ch}</c> is
+ the actual request.</p>
+ <p>The request is made into a message and sent to the gen_server.
+ <c>cast</c>, and thus <c>free</c>, then returns <c>ok</c>.</p>
+ <p>When the request is received, the gen_server calls
+ <c>handle_cast(Request, State)</c> which is expected to
+ return a tuple <c>{noreply, State1}</c>. <c>State1</c> is a new
+ value for the state of the gen_server.</p>
+ <code type="none">
+handle_cast({free, Ch}, Chs) ->
+ Chs2 = free(Ch, Chs),
+ {noreply, Chs2}.</code>
+ <p>In this case, the new state is the updated list of available
+ channels <c>Chs2</c>. The gen_server is now ready for new
+ requests.</p>
+ </section>
+
+ <section>
+ <title>Stopping</title>
+
+ <section>
+ <title>In a Supervision Tree</title>
+ <p>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 <seealso marker="sup_princ#shutdown">shutdown strategy</seealso> set in the supervisor.</p>
+ <p>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 <c>init</c> function. When ordered
+ to shutdown, the gen_server will then call the callback function
+ <c>terminate(shutdown, State)</c>:</p>
+ <code type="none">
+init(Args) ->
+ ...,
+ process_flag(trap_exit, true),
+ ...,
+ {ok, State}.
+
+...
+
+terminate(shutdown, State) ->
+ ..code for cleaning up here..
+ ok.</code>
+ </section>
+
+ <section>
+ <title>Stand-Alone Gen_Servers</title>
+ <p>If the gen_server is not part of a supervision tree, a stop
+ function may be useful, for example:</p>
+ <code type="none">
+...
+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.</code>
+ <p>The callback function handling the <c>stop</c> request returns
+ a tuple <c>{stop, normal, State1}</c>, where <c>normal</c>
+ specifies that it is a normal termination and <c>State1</c> is
+ a new value for the state of the gen_server. This will cause
+ the gen_server to call <c>terminate(normal,State1)</c> and then
+ terminate gracefully.</p>
+ </section>
+ </section>
+
+ <section>
+ <title>Handling Other Messages</title>
+ <p>If the gen_server should be able to receive other messages than
+ requests, the callback function <c>handle_info(Info, State)</c>
+ 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.</p>
+ <code type="none">
+handle_info({'EXIT', Pid, Reason}, State) ->
+ ..code to handle exits here..
+ {noreply, State1}.</code>
+ </section>
+</chapter>
+