Erlang/OTP gen_server template example

Erlang/OTP, what is this?

Hello!
Erlang is a functional programming language, process oriented, designed for performance in concurrent and distributed scenarios. OTP (standing for Open Telecom Platform) is a framework written for erlang. A framework is some code someone wrote, hopefully with good design patterns in mind to avoid reinventing the wheel every time. This is the case, the OTP framework was written to encourage good patters and robustness

OTP behaviours

Erlang behaviours are erlang’s way to abstract common patterns. Basically there are two modules, the behaviour itself with all the common functions, and the callback module, including the specific functionality that process provides by implementing the behaviour callbacks

There are basically four OTP behaviours you must know if you plan to write some quality erlang process modules

  • gen_server For implementing the server of a client-server relation.
  • gen_fsm For implementing finite state machines
  • gen_event For implementing event handling functionality, this one is a bit special as it is not intended to be a separate process
  • supervisor For implementing a supervisor in a supervision tree

OTP gen_server

The first behaviour we will explain is the gen_server. There are no surprises in what a gen_server is, yes, it is a server. Clients will ask it to process requests and it will provide the service to these clients, nothing new here

Let’s take a look at an empty, yet functioning, gen_server (you could use this as template)


% interface calls
-export([start/1, stop/0]).

% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).

%%====================================================================
%% Server interface
%%====================================================================
%% Booting server (and linking to it)
start(Pars) ->
io:format("Starting with ~p~n",[Pars]),
gen_server:start_link({local, ?MODULE}, ?MODULE, [Pars], []).

%% Stopping server asynchronously
stop() ->
io:format("Stopping~n"),
gen_server:cast(?MODULE, shutdown).

%%====================================================================
%% gen_server callbacks
%%====================================================================
init([Pars]) ->
io:format("Initializing with ~p~n",[Pars]),
process_flag(trap_exit, true),
{ok, initialized}.

%% Synchronous, possible return values
% {reply,Reply,NewState}
% {reply,Reply,NewState,Timeout}
% {reply,Reply,NewState,hibernate}
% {noreply,NewState}
% {noreply,NewState,Timeout}
% {noreply,NewState,hibernate}
% {stop,Reason,Reply,NewState}
% {stop,Reason,NewState}
handle_call(Message, From, State) ->
io:format("Generic call handler: '~p' from '~p' while in '~p'~n",[Message, From, State]),
{reply, ok, State}.

%% Asynchronous, possible return values
% {noreply,NewState}
% {noreply,NewState,Timeout}
% {noreply,NewState,hibernate}
% {stop,Reason,NewState}
%% normal termination clause
handle_cast(shutdown, State) ->
io:format("Generic cast handler: *shutdown* while in '~p'~n",[State]),
{stop, normal, State};
%% generic async handler
handle_cast(Message, State) ->
io:format("Generic cast handler: '~p' while in '~p'~n",[Message, State]),
{noreply, State}.

%% Informative calls
% {noreply,NewState}
% {noreply,NewState,Timeout}
% {noreply,NewState,hibernate}
% {stop,Reason,NewState}
handle_info(_Message, _Server) ->
io:format("Generic info handler: '~p' '~p'~n",[_Message, _Server]),
{noreply, _Server}.

%% Server termination
terminate(_Reason, _Server) ->
io:format("Generic termination handler: '~p' '~p'~n",[_Reason, _Server]).

%% Code change
code_change(_OldVersion, _Server, _Extra) -> {ok, _Server}.

Let’s compile and boot the server process, that is performed with the interface function start/1. The start method creates a server_template process. During the creation of this server the init/1 function is called

The start return value is the server process id 0.34.0. The server is now up ready to start processing requests. As an example we can request service with the gen_server:cast/2 function. This code lives in the OTP library, and by the name of the module manages to invoke our implementation of the callback. In our case it will end up running the server_template:handle_cast/2 with {do_something, "Parameter"} as message, as seen in next snippet

Finally we stop the server. The termination cleanup function will be invoked because we have requested the trap_exit flag

That closes the basic gen_server template example. Get familiar with this template code, it shows what a gen_server can do, process a synchronous call, process an asynchronous call, receive info or other messages, and execute termination code when trap_exit is enabled

 

Bytefilia