GenServer


    
GenServer abstracts the client-server interaction


GenServer is a process. it can be used to keep state and execute code
asynchronously. the goal of a GenServer is to abstract the “receive”
loop for developers. if you want to receive custom messages, always receive
them in handle_info/2


we can interact with the server by sending two types of messages:

   - call messages which expect a reply
   - cast messages which do not expect a reply

every time you do a GenServer.call/3, the client will send a message that
must be handled by the handle_call/3 callback in the GenServer. A cast/2
message must be handled by handle_cast/2




if you want a GenServer that works like a stack:


defmodule Stack
do

  use GenServer

  def handle_call(:pop, _from, [h | t]) do
    {:reply, h, t}
  end

  def handle_cast({:push, item}, state) do
    {:noreply, [item | state]}
  end

end



iex> {:ok, pid} = GenServer.start_link(Stack, ["hello"])

iex> GenServer.call(pid, :pop)
 "hello"

iex> GenServer.cast(pid, {:push, "world"})
 :ok

iex> GenServer.call(pid, :pop)
 "world"






             Callbacks

there are 6 callbacks required to be implemented in a GenServer. by adding
use GenServer to your module, Elixir will automatically define all 6 callbacks
for you, leaving it up to you to implement the ones you want to customize




               Name Registration

both start_link/3 and start/3 support the GenServer to register a name on
start via the :name option
registered names are also automatically cleaned up on termination. the
supported values are:

     * an atom - the GenServer is registered locally with the given name
     using Process.register/2

     * {:global, term}- the GenServer is registered globally with the
     given term

     * {:via, module, term} - the GenServer is registered with the given
     mechanism and name
       the :via option expects a module that exports register_name/2,
       unregister_name/1,
       whereis_name/1 and send/2

for example, we could start and register our Stack server locally as follows:



   iex> {:ok, _} = GenServer.start_link(Stack, ["hello"], name: MyStack)


   iex> GenServer.call(MyStack, :pop)
    "hello"



once the server is started, the remaining functions in this module (call/3,
cast/2, and friends) will also accept an atom, or any :global or :via
tuples. the following formats are supported:

      a pid
      an atom         if the server is locally registered
      {atom, node}       if the server is locally registered at another
      node
      {:global, term}       if the server is globally registered
      {:via, module, name}   if the server is registered through an alternative
      registry





               APIs


defmodule Stack
do

  use GenServer

  # Client

  def start_link(default) do
    GenServer.start_link(__MODULE__, default)
  end

  def push(pid, item) do
    GenServer.cast(pid, {:push, item})
  end

  def pop(pid) do
    GenServer.call(pid, :pop)
  end

  # Server (callbacks)

  def handle_call(:pop, _from, [h | t]) do
    {:reply, h, t}
  end
  def handle_call(request, from, state) do
    # the default implementation from GenServer
    super(request, from, state)
  end

  def handle_cast({:push, item}, state) do
    {:noreply, [item|state]}
  end
  def handle_cast(request, state) do
    # the default implementation from GenServer
    super(request, state)
  end

end




            Types

from :: {pid, term}

on_start ::  {:ok, pid} |  :ignore |  {:error, {:already_started, pid} | term}

option ::  {:debug, debug} |  {:name, name} |  {:timeout, timeout} |
{:spawn_opt, Process.spawn_opt}

server :: pid | name | {atom, node}



             Functions







call(server, term, timeout) :: term

   Makes a synchronous call to the server and waits for its reply

   The client sends the given request to the server and waits until a reply
   arrives or a timeout
   occurs. handle_call/3 will be called on the server to handle the request

   The server can be any of the values described in the Name Registration
   section of the module
   documentation

Timeouts

   The timeout is an integer greater than zero which specifies how many
   milliseconds to wait for a
   reply, or the atom :infinity to wait indefinitely. The default value is
   5000. If no reply is
   received within the specified time, the function call fails. If the caller
   catches the failure
   and continues running, and the server is just late with the reply, it
   may arrive at any time
   later into the caller’s message queue. The caller must in this case be
   prepared for this and
   discard any such garbage messages that are two-element tuples with a
   reference as the first
   element





cast(server, term) :: :ok

   Sends an asynchronous request to the server

   This function returns :ok without waiting for the destination server to
   handle the message
   Therefore it is unknown whether the destination server successfully
   handled the message. If the
   server is an atom without an associated process an ArgumentError is
   raised. In all other cases
   the function returns :ok regardless of whether the destination server
   (or node) exists. Note
   that {name, node()} can be used when an exception is not desired if no
   process is locally
   associated with the atom name

   handle_cast/2 will be called on the server to handle the request. In case
   the server is on a
   node which is not yet connected to the caller one, the call is going to
   block until a
   connection happens. This is different than the behaviour in OTP’s
   :gen_server where the message
   is sent by another process in this case, which could cause messages to
   other nodes to arrive
   out of order





multi_call([node], name :: atom, term, timeout) :: {replies :: [{node,
term}], bad_nodes :: [node]}

   Calls all servers locally registered as name at the specified nodes

   The request is first sent to every node and then we wait for the
   replies. This function returns
   a tuple containing the node and its reply as first element and all bad
   nodes as second element
   The bad nodes is a list of nodes that either did not exist, or where a
   server with the given
   name did not exist or did not reply

   Nodes is a list of node names to which the request is sent. The default
   value is the list of
   all known nodes

   To avoid that late answers (after the timeout) pollute the caller’s
   message queue, a middleman
   process is used to do the actual calls. Late answers will then be discarded
   when they arrive to
   a terminated process






reply(from, term) :: :ok

   Replies to a client

   This function can be used by a server to explicitly send a reply to a
   client that called call/3
   or multi_call/4. When the reply cannot be defined in the return value
   of handle_call/3

   The client must be the from argument (the second argument) received in
   handle_call/3 callbacks
   Reply is an arbitrary term which will be given back to the client as the
   return value of the
   call

   This function always returns :ok





start(module, any, options) :: on_start


start_link(module, any, options) :: on_start

   once the server is started, it calls the init/1 function in the given
   module passing the given
   args to initialize it. this function does not return until init/1 has
   returned

   a GenServer started with start_link/3 is linked to the parent process
   and will exit in case of crashes

   :name option is used for name registration
   if the option :timeout option is present, the server is allowed to spend
   the given milliseconds
   initializing or it will be terminated and the start function will return
   {:error, :timeout}

   If the server is successfully created and initialized, the function
   returns {:ok, pid}, where
   pid is the pid of the server. If a process with the specified server name
   already exists, the
   function returns {:error, {:already_started, pid}} with the pid of
   that process

   If the init/1 callback fails with reason, the function returns {:error,
   reason}. Otherwise, if
   it returns {:stop, reason} or :ignore, the process is terminated and the
   function returns
   {:error, reason} or :ignore, respectively


stop(server, reason :: term, timeout) :: :ok

   the terminate/2 callback will be invoked before exiting
   it returns :ok if the server terminates with the given reason
   if it terminates with another reason, the call will exit
   if the reason is any other than :normal, :shutdown or {:shutdown, _},
   an error report will be logged






whereis(server) :: pid | {atom, node} | nil

for example, to lookup a server process, monitor it and send a cast:

iex> p = GenServer.whereis(server)
iex> m = Process.monitor(p)
iex> GenServer.cast(p, :hello)




abcast([node], name :: atom, term) :: :abcast

   casts all servers locally registered as name at the specified nodes

   the function returns immediately and ignores nodes that do not exist,
   or where the server name
   does not exist








               Callbacks




    init(args :: term) ::
    {:ok, state} |
    {:ok, state, timeout | :hibernate} |
    :ignore |
    {:stop, reason :: any} when state: any

   start_link/3 (or start/3) will block until it returns

   args is the argument term (second argument) passed to start_link/3

   returning {:ok, state} will cause start_link/3 to return {:ok, pid}

   returning {:ok, state, timeout} is similar to {:ok, state} except
   handle_info(:timeout, state)
   will be called after timeout milliseconds if no messages are received
   within the timeout

   returning {:ok, state, :hibernate} is similar to {:ok, state} except the
   process is hibernated
   before entering the loop

   returning :ignore will cause start_link/3 to return :ignore and the
   process will exit normally
   without entering the loop or calling terminate/2. the main use cases for
   this are:
     * the GenServer is disabled by configuration but might be enabled later
     * an error occured and it will be handled by a different mechanism than
     the Supervisor

   returning {:stop, reason} will cause start_link/3 to return {:error,
   reason} and the process to
   exit with reason without entering the loop or calling terminate/2







handle_call(request :: term, from, state :: term) ::
  {:reply, reply, new_state} |
  {:reply, reply, new_state, timeout | :hibernate} |
  {:noreply, new_state} |
  {:noreply, new_state, timeout | :hibernate} |
  {:stop, reason, reply, new_state} |
  {:stop, reason, new_state} when reply: term, new_state: term, reason: term

   invoked to handle synchronous call/3 messages
   call/3 will block until a reply is received

   request is the request message sent by a call/3, from is a 2-tuple
   containing the caller’s pid
   and a term that uniquely identifies the call, and state is the current
   state of the GenServer

   Returning {:reply, reply, new_state} sends the response reply to the
   caller and continues the
   loop with new state new_state

   Returning {:reply, reply, new_state, timeout} is similar to {:reply,
   reply, new_state} except
   handle_info(:timeout, new_state) will be called after timeout milliseconds
   if no messages are
   received

   Returning {:reply, reply, new_state, :hibernate} is similar to {:reply,
   reply, new_state}
   except the process is hibernated and will continue the loop once a message
   is its message
   queue. If a message is already in the message queue this will be
   immediately. Hibernating a
   GenServer causes garbage collection and leaves a continuous heap that
   minimises the memory used
   by the process

   Hibernating should not be used aggressively as too much time could be
   spent garbage collecting
   Normally it should only be used when a message is not expected soon and
   minimising the memory
   of the process is shown to be beneficial

   Returning {:noreply, new_state} does not send a response to the caller
   and continues the loop
   with new state new_state. The response must be sent with reply/2

   There are three main use cases for not replying using the return value:
     * To reply before returning from the callback because the response is
     known before calling a
       slow function
     * To reply after returning from the callback because the response is
     not yet available
     * To reply from another process, such as a task

   When replying from another process the GenServer should exit if the other
   process exits without
   replying as the caller will be blocking awaiting a reply

   Returning {:noreply, new_state, timeout | :hibernate} is similar to
   {:noreply, new_state}
   except a timeout or hibernation occurs as with a :reply tuple

   Returning {:stop, reason, reply, new_state} stops the loop and terminate/2
   is called with
   reason reason and state new_state. Then the reply is sent as the response
   to call and the
   process exits with reason reason

   Returning {:stop, reason, new_state} is similar to {:stop, reason, reply,
   new_state} except a
   reply is not sent





handle_cast(request :: term, state :: term) ::
  {:noreply, new_state} |
  {:noreply, new_state, timeout | :hibernate} |
  {:stop, reason :: term, new_state} when new_state: term

   Invoked to handle asynchronous cast/2 messages

   request is the request message sent by a cast/2 and state is the current
   state of the
   GenServer

   Returning {:noreply, new_state} continues the loop with new state new_state

   Returning {:noreply, new_state, timeout} is similar to {:noreply, reply,
   new_state} except
   handle_info(:timeout, new_state) will be called after timeout milliseconds
   if no messages are
   received

   Returning {:noreply, new_state, :hibernate} is similar to {:noreply,
   new_state} except the
   process is hibernated before continuing the loop. See handle_call/3 for
   more information

   Returning {:stop, reason, new_state} stops the loop and terminate/2 is
   called with the reason
   reason and state new_state. The process exits with reason reason






handle_info(msg :: :timeout | term, state :: term) ::
  {:noreply, new_state} |
  {:noreply, new_state, timeout | :hibernate} |
  {:stop, reason :: term, new_state} when new_state: term

   Invoked to handle all other messages

   msg is the message and state is the current state of the GenServer. When
   a timeout occurs the
   message is :timeout

   Return values are the same as handle_cast/2









terminate(reason, state :: term) :: term when reason: :normal | :shutdown |
{:shutdown, term} | term

   Invoked when the server is about to exit. It should do any cleanup required

   reason is exit reason and state is the current state of the GenServer

   The return value is ignored

   terminate/2 is called if a callback (except init/1) returns a :stop tuple,
   raises, calls
   Kernel.exit/1 or returns an invalid value. It may also be called if the
   GenServer traps exits
   using Process.flag/2 and the parent process sends an exit signal

   If part of a supervision tree a GenServer‘s Supervisor will send an
   exit signal when shutting
   it down. The exit signal is based on the shutdown strategy in the child’s
   specification
   If it is :brutal_kill the GenServer is killed and so terminate/2 is
   not called
   However if it is a timeout the Supervisor will send the exit signal
   :shutdown and the GenServer
   will have the duration of the timeout to call terminate/2 - if the process
   is still alive after
   the timeout it is killed

   If the GenServer receives an exit signal (that is not :normal) from any
   process when it is not
   trapping exits it will exit abruptly with the same reason and so not call
   terminate/2. Note
   that a process does NOT trap exits by default and an exit signal is sent
   when a linked process
   exits or its node is disconnected

   Therefore it is not guaranteed that terminate/2 is called when a GenServer
   exits. For such
   reasons, we usually recommend important clean-up rules to happen in
   separated processes either
   by use of monitoring or by links themselves. For example if the GenServer
   controls a port (e.g
   :gen_tcp.socket) or File.io_device, they will be closed on receiving a
   GenServer‘s exit signal
   and do not need to be closed in terminate/2

   If reason is not :normal, :shutdown nor {:shutdown, term} an error is logged



code_change(old_vsn, state :: term, extra :: term) ::
  {:ok, new_state :: term} |
  {:error, reason :: term} when old_vsn: term | {:down, term}

   old_vsn is the previous version of the module (defined by the @vsn
   attribute) when upgrading
   When downgrading the previous version is wrapped in a 2-tuple with first
   element :down. state
   is the current state of the GenServer and extra is any extra data required
   to change the state

   Returning {:ok, new_state} changes the state to new_state and the code
   change is successful

   Returning {:error, reason} fails the code change with reason reason and
   the state remains as
   the previous state

   If code_change/3 raises the code change fails and the loop will continue
   with its previous
   state. Therefore this callback does not usually contain side effects