Search code examples
erlangerlang-otpgen-server

What is the gen_server's State life cycle


I am new learner on Erlang, I have questions about Erlang variable's life cycle.

Reference from Erlang gen_server comunication

-module(wy).
-compile(export_all).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-behaviour(gen_server).
-record(state, {id ,m, succ, pred}).

start(Name, M) ->
    gen_server:start_link({local, Name}, ?MODULE, [Name, M], []).

init([Name, M]) ->
    {ok, #state{id = Name, m = M}}.

handle_call({get_server_info}, _Frome, State) ->
    {reply, State, State};
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

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

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

terminate(_Reason, _State) ->
    ok.

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


get_server_info(ServerName) ->
    gen_server:call(ServerName, {get_server_info}).

What is the Variable "State" life cycle?

We can see variable "State" is reused from handle_call and handle_cast. First of all, are these "State" is the same one which is initialized from init() function "#state{id = Name, m = M}"?

If so, is this "State" a global variable? When will this "State" be destroyed.

Does Erlang have global variables?


Solution

  • Your intuition is correct, although I think it's worth pointing out that the variable State is technically not the same everywhere but it does reference the same value everywhere. Each of your callbacks (i.e. inti/1, handle_call/3, etc.) receive State as a parameter and they must return a (possibly new) State as part of their results.

    To understand how this works, you need to know what gen_server does. In an super-extremely-oversimplified way, what gen_server is doing when you call gen_server:start_link/4 is:

    % Ignoring stuff here, for simplicity
    start_link(_Name, Mod, InitArg, _Options) ->
      spawn(
        fun() ->
          {ok, State} = Mod:init(InitArg),
          loop(Mod, State) %% This is where the result of your init/1 function goes
        end).
    
    loop(Mod, State) ->
      NextState =
        receive
          {call, From, Msg} ->
            % Here, it gives you the current state and expects you to return
            % a new one.
            {reply, Reply, NewState} = Mod:handle_call(Msg, From, State),
            NewState;
          {cast, Msg} ->
            % Here, it gives you the current state and expects you to return
            % a new one.
            {noreply, NewState} = Mod:handle_cast(Msg, State),
            NewState;
          Info ->
            % Here, it gives you the current state and expects you to return
            % a new one.
            {noreply, NewState} = Mod:handle_info(Msg, State),
            NewState;
          {stop, Reason} ->
            Mod:terminate(Reason, State),
            exit(Reason)
        end,
      loop(Mod, NextState). % Then it keeps on looping with the new state
    

    As you can see, the value of the State is local to the gen_server process, it is passed to each callback and it's replaced with the result of each callback to keep looping until the server is terminated. Of course, gen_server's code is not that simple (This talk provides a great explanation for this - in Spanish, I know, sorry).

    Hope this helps :)