5338 Gen server - Telecomix Crypto Munitions Bureau

Gen server

From Telecomix Crypto Munitions Bureau

Jump to: navigation, search

gen_server is used to create worker processes that

  • (optional) Communicates with a group of client processes
  • (optional) Maintains a state
  • Performs computations
  • Replies to messages


[edit] How it works

There is one module in the OTP that is named gen_server that simplifies the creation and handling of servers.

Your code implements the gen_server "behaviour" (one form of worker pattern).

Others can then easily send messages to your server using the gen_server API.

[edit] API mapping between gen_server and your code


GEN_SERVER       YOUR CODE            MOAR??

start_link()     init()               starts & attach your server to a supervisor
start()          init()               starts your server in stand-alone mode
call()           handle_call()        send/handle synchronous message
multi_call()     handle_call()            send message to multiple servers at once
cast()           handle_cast()        send/handle asynchronous message
abcast()         handle_cast()            send message to multiple servers at once
reply()          ---                  used to avoid handle_info() in handle_call()
enter_loop()     ---                  turns a non-server to a gen_server-server
---              handle_info()        informs your code of timeouts / nonstandard messages

Generally, other code will call gen_server:call() or gen_server:cast() when they want to communicate with your server. Thus, handle_call() and handle_cast() is where the shit happens.

[edit] gen_server API

This is the interface that the gen_server-module has.

The API is described in detail at erldocs.org.

  1. gen_server:start_link/3 - attaches a gen_server to a supervision tree. Optionally registers the name of the gen_server module.
    • Will call MODULE:init and wait until it completes before returning
    • Returns: {ok,Pid} or ignore or {error,Error}
    • Can optionally register a name for the gen_server, so that it can receive messages from multi_call or abcast
  2. gen_server:start/3-4 - creates a standalone gen_server (not part of supervision tree)
    • Works very much like start_link
  3. gen_server:call/2-3 - sends synchronous message to gen_server module.
    • Will call MODULE:handle_call
    • Returns: whatever your code returns (can be any erlang term)
  4. gen_server:multi_call/2-4 - sends synchronous message to a group of gen_servers
    • Will call MODULE:handle_call
    • Returns: a list of {Node, Reply}. One entry for each reply.
    • Can send message to ALL gen_servers registered under a name at ALL nodes in a cluster, or to all gen_servers registered under a name in a specified set of nodes in the cluster
  5. gen_server:cast/2 - sends asynchronous message to a gen_server
    • Will call MODULE:handle_cast
    • Returns: ok (the reply might come at a later time, or not at all.)
  6. gen_server:abcast/2-3 - sends asynchronous messages to a group of gen_servers
    • Will call MODULE:handle_cast
    • Returns: ok (the replies might come at a later time, or not at all.)
    • Can send message to ALL gen_servers registered under a name at ALL nodes in a cluster, or to all gen_servers registered under a name in a specified set of nodes in the cluster
  7. gen_server:reply/2 - should be used to avoid timeouts, see handle_info()
    • used to reply to a call or multi_call if the reply should be sent before MODULE:handle_call is complete.
    • Returns: the return value of this function should be ignored.
  8. gen_server:enter_loop/3-5 - turns a non-gen_server process into a gen_server-process.
    • If you know how to use this function, you dont need to look here

[edit] MODULE Interface

This is the interface that you should implement in your code.

Functions not implemented by your code will use the gen_server default, which does nothing. (generates warnings)

Below are just notes that I made for myself, to more quickly remember what i read.



init(State) ->
   returns any of
      {ok, State}
      {ok, State, Timeout}       - operations will take less than Timeout ms or else handle_info()
      {ok, State, hibernate}     - hibernate = operations will take quite some time, optimize for this
      {stop, Reason}             - crash
      ignore                     - do not use


%%%% gen_server:call() + gen_server:multi_call()
handle_call(Request, From, State) ->
   returns any of
      {reply,Reply,NewState}
      {reply,Reply,NewState,Timeout}
      {reply,Reply,NewState,hibernate}
      {noreply,NewState}                - send no reply. use gen_server:reply() to avoid handle_info()
      {noreply,NewState,Timeout}
      {noreply,NewState,hibernate}
      {stop,Reason,Reply,NewState}      - crash
      {stop,Reason,NewState}            - crash


%%%% gen_server:cast() + gen_server:abcast()
handle_cast(Message, State) ->
   returns any of
      {noreply,NewState}
      {noreply,NewState,Timeout}
      {noreply,NewState,hibernate}
      {stop,Reason,NewState}             - crash


%%%% notifies you that you either failed to reply before Timeout
%%%%                              OR received a nonstandard message
handle_info(Info, State) ->
   returns any of
      {noreply,NewState}
      {noreply,NewState,Timeout}
      {noreply,NewState,hibernate}
      {stop,Reason,NewState}


%%%% does not matter what you return from this function
%%%% is only used to notify your code that it is about to shut down
%%%% make sure to save your state somehow
terminate(Reason, State) -> 


%%%% notifies your process that it should switch to a new piece of code
code_change(PreviousVersion, State, Extra) ->


%%%% if you know what this one does, you dont need to read this
format_status(Opt, [PDict, State]) ->


[edit] Example

Server that computes whole-integer squares of numbers sent to it. State is set to the last computed Square.

-module(name_of_module).
-behaviour(gen_server).

-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).



%% Make life simpler %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

start_link() ->
   gen_server:start_link(?MODULE, [], []).



%% gen_server interface for our module %%%%%%%%%%%%%%%%%%

init(_State) ->
   {ok, []}.



handle_call(Request, _From, State) ->
   if is_integer(Request) ->
         NewState = compute_square(Request),
         {reply, NewState, NewState};
      true ->
         {reply, error, State}
   end.



handle_cast(_Message, State) ->
    {noreply, State}.



handle_info(_Info, State) ->
    {noreply, State}.



terminate(_Reason, State) ->
    State.



code_change(_OldVsn, State, _Extra) ->
    {ok, State}.



%% Private functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

compute_square(Number) ->
   Number * Number.



[edit] Sources

Personal tools
0