1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- This document was generated using DocBuilder 3.3.3 -->
<HTML>
<HEAD>
<TITLE>Gen_Server Behaviour</TITLE>
<SCRIPT type="text/javascript" src="../../doc/erlresolvelinks.js">
</SCRIPT>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#FF00FF"
ALINK="#FF0000">
<CENTER>
<A HREF="http://www.erlang.se"><IMG BORDER=0 ALT="[Ericsson AB]" SRC="min_head.gif"></A>
</CENTER>
<A NAME="2"><!-- Empty --></A>
<H2>2 Gen_Server Behaviour</H2>
<A NAME="gen_server"><!-- Empty --></A>
<P>This chapter should be read in conjunction with
<CODE>gen_server(3)</CODE>, where all interface functions and callback
functions are described in detail.<A NAME="2.1"><!-- Empty --></A>
<H3>2.1 Client-Server Principles</H3>
<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>
<CENTER>
<IMG ALT="clientserver" SRC="clientserver.gif"><BR>
<EM><A NAME="clientserver"><!-- Empty --></A>Client-Server Model
</EM>
</CENTER>
<A NAME="2.2"><!-- Empty --></A>
<H3>2.2 Example</H3>
<P>An example of a simple server written in plain Erlang was
given in <A HREF="des_princ.html#ch1">Overview</A>.
The server can be re-implemented using <CODE>gen_server</CODE>,
resulting in this callback module:<A NAME="ex"><!-- Empty --></A>
<PRE>
-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}.
</PRE>
<P>The code is explained in the next sections.<A NAME="2.3"><!-- Empty --></A>
<H3>2.3 Starting a Gen_Server</H3>
<P>In the example in the previous section, the gen_server is started
by calling <CODE>ch3:start_link()</CODE>:
<PRE>
start_link() ->
gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}
</PRE>
<P><CODE>start_link</CODE> calls the function
<CODE>gen_server:start_link/4</CODE>. This function spawns and links to
a new process, a gen_server.
<P>
<UL>
<LI>
The first argument <CODE>{local, ch3}</CODE> specifies the name. In
this case, the gen_server will be locally registered as
<CODE>ch3</CODE>.<BR>
If the name is omitted, the gen_server is not registered.
Instead its pid must be used. The name could also be given
as <CODE>{global, Name}</CODE>, in which case the gen_server is
registered using <CODE>global:register_name/2</CODE>.<BR>
</LI>
<LI>
The second argument, <CODE>ch3</CODE>, is the name of the callback
module, that is the module where the callback functions are
located.<BR>
In this case, the interface functions (<CODE>start_link</CODE>,
<CODE>alloc</CODE> and <CODE>free</CODE>) are located in the same module
as the callback functions (<CODE>init</CODE>, <CODE>handle_call</CODE> and
<CODE>handle_cast</CODE>). This is normally good programming
practice, to have the code corresponding to one process
contained in one module.<BR>
</LI>
<LI>
The third argument, [], is a term which is passed as-is to
the callback function <CODE>init</CODE>. Here, <CODE>init</CODE> does not
need any indata and ignores the argument.<BR>
</LI>
<LI>
The fourth argument, [], is a list of options. See
<CODE>gen_server(3)</CODE> for available options.<BR>
</LI>
</UL>
<P>If name registration succeeds, the new gen_server process calls
the callback function <CODE>ch3:init([])</CODE>. <CODE>init</CODE> is expected
to return <CODE>{ok, State}</CODE>, where <CODE>State</CODE> is the internal
state of the gen_server. In this case, the state is the available
channels.
<PRE>
init(_Args) ->
{ok, channels()}.
</PRE>
<P>Note that <CODE>gen_server:start_link</CODE> is synchronous. It does
not return until the gen_server has been initialized and is ready
to receive requests.
<P><CODE>gen_server:start_link</CODE> must be used if the gen_server is
part of a supervision tree, i.e. is started by a supervisor.
There is another function <CODE>gen_server:start</CODE> to start a
stand-alone gen_server, i.e. a gen_server which is not part of a
supervision tree.<A NAME="2.4"><!-- Empty --></A>
<H3>2.4 Synchronous Requests - Call</H3>
<P>The synchronous request <CODE>alloc()</CODE> is implemented using
<CODE>gen_server:call/2</CODE>:
<PRE>
alloc() ->
gen_server:call(ch3, alloc).
</PRE>
<P><CODE>ch3</CODE> is the name of the gen_server and must agree with
the name used to start it. <CODE>alloc</CODE> is the actual request.
<P>The request is made into a message and sent to the gen_server.
When the request is received, the gen_server calls
<CODE>handle_call(Request, From, State)</CODE> which is expected to
return a tuple <CODE>{reply, Reply, State1}</CODE>. <CODE>Reply</CODE> is
the reply which should be sent back to the client, and
<CODE>State1</CODE> is a new value for the state of the gen_server.
<PRE>
handle_call(alloc, _From, Chs) ->
{Ch, Chs2} = alloc(Chs),
{reply, Ch, Chs2}.
</PRE>
<P>In this case, the reply is the allocated channel <CODE>Ch</CODE> and
the new state is the set of remaining available channels
<CODE>Chs2</CODE>.
<P>Thus, the call <CODE>ch3:alloc()</CODE> returns the allocated channel
<CODE>Ch</CODE> and the gen_server then waits for new requests, now
with an updated list of available channels.<A NAME="2.5"><!-- Empty --></A>
<H3>2.5 Asynchronous Requests - Cast</H3>
<P>The asynchronous request <CODE>free(Ch)</CODE> is implemented using
<CODE>gen_server:cast/2</CODE>:
<PRE>
free(Ch) ->
gen_server:cast(ch3, {free, Ch}).
</PRE>
<P><CODE>ch3</CODE> is the name of the gen_server. <CODE>{free, Ch}</CODE> is
the actual request.
<P>The request is made into a message and sent to the gen_server.
<CODE>cast</CODE>, and thus <CODE>free</CODE>, then returns <CODE>ok</CODE>.
<P>When the request is received, the gen_server calls
<CODE>handle_cast(Request, State)</CODE> which is expected to
return a tuple <CODE>{noreply, State1}</CODE>. <CODE>State1</CODE> is a new
value for the state of the gen_server.
<PRE>
handle_call({free, Ch}, Chs) ->
Chs2 = free(Ch, Chs),
{noreply, Chs2}.
</PRE>
<P>In this case, the new state is the updated list of available
channels <CODE>Chs2</CODE>. The gen_server is now ready for new
requests.<A NAME="2.6"><!-- Empty --></A>
<H3>2.6 Stopping</H3>
<A NAME="2.6.1"><!-- Empty --></A>
<H4>2.6.1 In a Supervision Tree</H4>
<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 <A HREF="sup_princ.html#shutdown">shutdown
strategy</A> set in the supervisor.
<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 <CODE>init</CODE> function. When ordered
to shutdown, the gen_server will then call the callback function
<CODE>terminate(shutdown, State)</CODE>:
<PRE>
init(Args) ->
...,
process_flag(trap_exit, true),
...,
{ok, State}.
...
terminate(shutdown, State) ->
..code for cleaning up here..
ok.
</PRE>
<A NAME="2.6.2"><!-- Empty --></A>
<H4>2.6.2 Stand-Alone Gen_Servers</H4>
<P>If the gen_server is not part of a supervision tree, a stop
function may be useful, for example:
<PRE>
...
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.
</PRE>
<P>The callback function handling the <CODE>stop</CODE> request returns
a tuple <CODE>{stop, normal, State1}</CODE>, where <CODE>normal</CODE>
specifies that it is a normal termination and <CODE>State1</CODE> is
a new value for the state of the gen_server. This will cause
the gen_server to call <CODE>terminate(normal,State1)</CODE> and then
terminate gracefully.<A NAME="2.7"><!-- Empty --></A>
<H3>2.7 Handling Other Messages</H3>
<P>If the gen_server should be able to receive other messages than
requests, the callback function <CODE>handle_info(Info, State)</CODE>
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.
<PRE>
handle_info({'EXIT', Pid, Reason}, State) ->
..code to handle exits here..
{noreply, State1}.
</PRE>
<CENTER>
<HR>
<SMALL>
Copyright © 1991-2006
<A HREF="http://www.erlang.se">Ericsson AB</A><BR>
</SMALL>
</CENTER>
</BODY>
</HTML>
|