From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- .../doc/design_principles/gen_server_concepts.xml | 269 +++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 system/doc/design_principles/gen_server_concepts.xml (limited to 'system/doc/design_principles/gen_server_concepts.xml') 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 @@ + + + + +
+ + 19972009 + Ericsson AB. All Rights Reserved. + + + 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. + + + + Gen_Server Behaviour + + + + + gen_server_concepts.xml +
+ +

This chapter should be read in conjunction with + gen_server(3), + where all interface functions and callback + functions are described in detail.

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

+ + + Client-Server Model + +
+ +
+ 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.

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

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

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

+
+
+ +
+ 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}. +
+
+ -- cgit v1.2.3