send message to gen_server before init - erlang

I am new to erlang, I having following code:
-module (test_srv).
-behaviour (gen_server).
-export ([start_link/0, init/1, handle_info/2]).
start_link() ->
gen_server:start_link(?MODULE, [], []).
init([]) ->
self() ! asdasd,
{ok, new_state}.
handle_info(Msg, State) ->
io:format("server got ~p , now state is ~p~n", [Msg, State]),
{noreply, State}.
I test in erl shell:
1> {_, P} = test_srv:start_link().
server got asdasd , now state is new_state
The problem is, When send a message to server when the server is not initialised and not readly, how dose gen_server handle the message? I have following guesses:
gen_server handle the message immediately, and send the message to handle_info callback, but will lose initialised state in init callback
gen_server store the message if not server initialised, and send message after server initialised.
I wanna to know how erlang or gen_server handle this problem? what is the principle of handle message?

I'm guessing by server is not initialised you mean the rest of the init function being executed. In that case your second guess is correct. It's guaranteed that the handle_info will be executed after init has returned. Since the gen_server is a single process, and you're already executing init, the messages sent to itself from init will only be processed by gen_server after init has finished executing.

Related

Unsupervised gen_server doesn't call terminate when it receives exit signal

gen_server documentation on Module:terminate callback says:
Even if the gen_server process is not part of a supervision tree, this
function is called if it receives an 'EXIT' message from its parent.
Reason is the same as in the 'EXIT' message.
Here is my handle_info and terminate function:
handle_info(UnknownMessage, State) ->
io:format("Got unknown message: ~p~n", [UnknownMessage]),
{noreply, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]).
I start this server using gen_server:start. I assume when I call erlang:exit(Pid, fuckoff), it should call terminate callback function. But it shows:
Got unknown message: {'EXIT',<0.33.0>,fuckoff}
Which means it is calling handle_info. But when I call gen_server:stop, everything works as mentioned in documentation. I'm calling my gen_server from shell. Would you please clarify this?
[UPDATE]
Here is source code of decode_msg function inside gen_server. If it receives any 'EXIT' message it should call terminate function:
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
{'EXIT', Parent, Reason} ->
terminate(Reason, Name, Msg, Mod, State, Debug);
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, State, Mod);
_Msg ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
Name, {in, Msg}),
handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.
In my case it doesn't call terminate function.
[UPDATE]
When I start gen_server using gen_server:start_link(), sending an exit signal using erlang:exit(Pid, Reason) will result in calling terminate call back function which is an expected behaviour. It seems there is a difference in interpreting an exit signal whether a process is linked to its parent or not.
Short answer:
If you call the exit/2 function from inside the gen_server actor, it behaves as expected based on the documentation and the terminate/2 callback will be called.
Long answer:
When you send the exit message from the shell, the Parent value of exit tuple is set to the shell process id, on the other hand when you start the gen_server process form shell its Parent value is set to its own process id, not the shell process id, therefore when it gets the exit message it doesn't match the second clause of the receive block in decode_msg/8 function so the terminate/6 function is not called and finally the next clause is matched which is calling handle_msg/5 function.
Recommendation:
For getting the terminate/3 callback called even by sending an exit message to the gen_server process, you can trap the exit message in handle_info/2 and then return with stop tuple as follows:
init([]) ->
process_flag(trap_exit, true),
{ok, #state{}}.
handle_info({'EXIT', _From, Reason}, State) ->
io:format("Got exit message: ~p~n", []),
{stop, Reason, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]),
%% do cleanup ...
ok.
When you start your gen_server, its a simple process, so, erlang:exit/1 or erlang:exit/2 work as expected.
If Pid is not trapping exits, Pid itself exits with exit reason Reason.
If Pid is trapping exits, the exit signal is transformed into a message {'EXIT', From, Reason} and delivered to the message queue of Pid.
So, currently your code trap 'EXIT' signal because this one is send like any other message into the mailbox and match handle_info/2 wildcard pattern.
If you want more information about that, you can read gen_server source code and see how it works. You can also find your problem described in this code.

How can I know when it's the last cycle of my process restarted by the supervisor in erlang

I have a simple_one_for_one supervisor which has gen_fsm children.
I want each gen_fsm child to send a message only on the last time it terminates.
Is there any way to know when is the last cycle?
here's my supervisor:
-module(data_sup).
-behaviour(supervisor).
%% API
-export([start_link/0,create_bot/3]).
%% Supervisor callbacks
-export([init/1]).
%%-compile(export_all).
%%%===================================================================
%%% API functions
%%%===================================================================
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
RestartStrategy = {simple_one_for_one, 0, 1},
ChildSpec = {cs_fsm, {cs_fsm, start_link, []},
permanent, 2000, worker, [cs_fsm]},
Children = [ChildSpec],
{ok, {RestartStrategy, Children}}.
create_bot(BotId, CNPJ,Pid) ->
supervisor:start_child(?MODULE, [BotId, CNPJ, Pid]).
the Pid is the Pid of the process which starts the superviser and gives orders to start the children.
-module(cs_fsm).
-behaviour(gen_fsm).
-compile(export_all).
-define(SERVER, ?MODULE).
-define(TIMEOUT, 5000).
-record(params, {botId, cnpj, executionId, pid}).
%%%===================================================================
%%% API
%%%===================================================================
start_link(BotId, CNPJ, Pid) ->
io:format("start_link...~n"),
Params = #params{botId = BotId, cnpj = CNPJ, pid = Pid},
gen_fsm:start_link(?MODULE, Params, []).
%%%===================================================================
%%% gen_fsm callbacks
%%%===================================================================
init(Params) ->
io:format("initializing~n"),
process_flag(trap_exit, true),
{ok, requesting_execution, Params, 0}.
requesting_execution(timeout,Params) ->
io:format("erqusting execution"),
{next_state, finished, Params,?TIMEOUT}.
finished(timeout, Params) ->
io:format("finished :)~n"),
{stop, normal, Params}.
terminate(shutdown, _StateName, Params) ->
Params#params.pid ! {terminated, self(),Params},
ok;
terminate(_Reason, _StateName, Params) ->
ok.
my point is that if the process fails in any of the states it should send a message only if it is the last time it is restarted by the supervisor (according to its restart strategy).
If the gen_fsm fails, does it restart from the same state with same state data? If not how can I cause it to happen?
You can add sending the message to the Module:terminate/3 function which is called when one of the StateName functions returns {stop,Reason,NewStateData} to indicate that the gen_fsm should be stopped.
gen_fsm is a finite state machine so you decide how it transitions between states. Something that triggers the last cycle may also set something in the StateData that is passed to Module:StateName/3 so that the function that handles the state knows it's the last cycle. It's hard to give a more specific answer unless you provide some code which we could analyze and comment on.
EDIT after further clarification:
Supervisor doesn't notify its children which time it has restarted them and it also can't notify the child that it's the last restart. This later is simply because it doesn't know that it's going to be the last until the supervisor process actually crashes once more, which the supervisor can't possibly predict. Only after the child crashed supervisor can calculate how many times the child crashed during a period of time and if it is allowed to restart the child once more or if that was the last restart and now it's time for the supervisor to die as well.
However, nothing is stopping the child from registering, e.g. in an ETS table, how many times it has been restarted. But it of course won't help with deducting which restart is the last one.
Edit 2:
When the supervisor restarts the child it starts it from scratch using the standard init function. Any previous state of the child before it crashed is lost.
Please note that a crash is an exceptional situation and it's not always possible to recover the state, because the crash could have corrupted the state. Instead of trying to recover the state or asking supervisor when it's done restarting the child, why not to prevent the crash from happening in the first place? You have two options:
I. Use try/catch to catch any exceptional situations and act accordingly. It's possible to catch any error that would otherwise crash the process and cause supervisor to restart it. You can add try/catch to any entry function inside the gen_fsm process so that any error condition is caught before it crashes the server. See example function 1 or example function 2:
read() ->
try
try_home() orelse try_path(?MAIN_CFG) orelse
begin io:format("Some Error", []) end
catch
throw:Term -> {error, Term}
end.
try_read(Path) ->
try
file:consult(Path)
catch
error:Error -> {error, Error}
end.
II. Spawn a new process to handle the job and trap EXIT signals when the process dies. This allows gen_fsm to handle a job asynchronously and handle any errors in a custom way (not necessarily by restarting the process as a supervisor would do). This section titled Error Handling explains how to trap exit signals from child processes. And this is an example of trapping signals in a gen_server. Check the handle_info function that contains a few clauses to trap different types of EXIT messages from children processes.
init([Cfg, Id, Mode]) ->
process_flag(trap_exit, true),
(...)
handle_info({'EXIT', _Pid, normal}, State) ->
{noreply, State};
handle_info({'EXIT', _Pid, noproc}, State) ->
{noreply, State};
handle_info({'EXIT', Pid, Reason}, State) ->
log_exit(Pid, Reason),
check_done(error, Pid, State);
handle_info(_, State) ->
{noreply, State}.

How to make a gen_server reply with a message?

I have the gen_server shown below. It works for the most part. However when I start it from the shell the replies come right back to the shell prompt. I would have expected them to be sent as messages back to the shells pid and then I would use flush() to see them.
What do I have to change in order to have the foo_worker send its replies as messages ?
-module(foo_worker).
-behaviour(gen_server).
%% API
-export([start_link/1, start/1, init/1, send/3, die/1]).
-export([handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
%%%-------------------------------------------------------------------
send(Worker, Ref, Counter) ->
gen_server:call(Worker, {inc, Ref, Counter}).
die(Worker) ->
gen_server:cast(Worker, die).
%%%-------------------------------------------------------------------
start_link(Limit) ->
gen_server:start_link(?MODULE, [Limit], []).
start(Limit) ->
gen_server:start(?MODULE, [Limit], []).
init([Limit]) ->
{ok, Limit}.
handle_call(_, _, Limit) when Limit =< 0 ->
exit({worker, eol});
handle_call({inc, Ref, Data}, From, Limit) ->
io:format("From ~p~n", [From]),
{reply, {Ref, updated, Data+1}, Limit - 1}.
handle_cast(die, _) ->
io:format("~p Dying ~n",[self()]),
exit(normal).
handle_info(Info, State) ->
io:format("Unkown message ~p for state ~p~n", [Info, State]).
terminate(Reason, State) ->
io:format("~p Died because ~p with state ~p~n", [self(), Reason, State]).
The whole point of gen_server:call/2,3 is to wrap into a function call the passing of a message into a gen_server process and the reception of its reply. If you want to deal only with messages, don't use gen_server:call/2,3 but rather have the caller invoke gen_server:cast/2 and include the caller pid in the message:
send(Worker, Ref, Counter) ->
gen_server:cast(Worker, {inc, Ref, Counter, self()}).
Then have gen_server:handle_cast/2 understand that message and use the pid send the reply back to the caller:
handle_cast({inc, Ref, Data, From}, Limit) ->
From ! {Ref, updated, Data+1},
{noreply, Limit-1}.
By the way, note that when you choose this sort of approach, you need to deal with possible failure. If you pass a message to the gen_server process but it dies before it sends you a reply, you need to make sure the caller doesn't sit and wait forever for a reply that will never arrive. The best way to do this is with a monitor — you can have the caller monitor the gen_server process before sending it a message and demonitor it once it receives the reply. If the gen_server process dies, the caller will get a DOWN message instead (see the monitor documentation for details). Also note that by doing this you're reimplementing a bunch of what gen_server:call/2,3 already does for you.

gen_server not getting messages after httpc call

I have one process which sends a pause message to a gen_server like so:
Results = [gen_server:cast(Child, pause) ||
{Id, Child, _Type, _Modules} <- supervisor:which_children(?SERVER),
?IGNORE(Id) == false],
In my gen_server, I catch these messages in my handle_cast as follows:
handle_cast(pause, #state{task=#task{server=Serv,
service=Srv,
description=Desc}}=State) ->
lager:info("Suspending ~s, ~s, ~s.",[Serv, Srv, Desc]),
{noreply, State#state{suspended=true}};
handle_cast(Msg, State) ->
lager:error("Url Poller received unexpected cast message: ~p",[Msg]),
{noreply, State}.
What's really strange is that fairly frequently one of my gen_servers doesn't seem to receive the pause message -- I get no lager message and the process in question will not respond to subsequent attempts to pause (or resume).
Any ideas about what might be going on?
The gen_server is very simple, it uses erlang:send_after/3 to send itself a "poll" message. Upon receiving this poll message, if not paused, it hits a url and saves the response to an ETS and fires off another erlang:send_after/3 to poll again after an appropriate interval. If its paused, it simply fires off another erlang:send_after?3
All pause does is set the state to paused = true
Using observer, the stuck process shows that the current function is httpc:handle_answer and that the message queue is backing up
Sate Tab: Information "Timed out"
Tip "system messages are probably not treated by this process"
the top of the stack trace shows
httpc:handle_answer httpc.erl:636
I picked the code of httpc:handle_answer from github erlang otp inets http client:
(Note: it is not the same version as yours since the function goes from line 616 to 631)
handle_answer(RequestId, false, _) ->
{ok, RequestId};
handle_answer(RequestId, true, Options) ->
receive
{http, {RequestId, saved_to_file}} ->
?hcrt("received saved-to-file", [{request_id, RequestId}]),
{ok, saved_to_file};
{http, {RequestId, {_,_,_} = Result}} ->
?hcrt("received answer", [{request_id, RequestId},
{result, Result}]),
return_answer(Options, Result);
{http, {RequestId, {error, Reason}}} ->
?hcrt("received error", [{request_id, RequestId},
{reason, Reason}]),
{error, Reason}
end.
So the process is waiting for a message (coming after a call to httpc_manager:request(Request, profile_name(Profile) which has returned {ok, RequestId}), and this message does not come or it has a wrong format. Can you check the values of the parameters and the message queue?
headers which contained value other than string caused the httpc_handler exited. But after that, the caller hung at the 'receive' in httpc:handle_answer/3 forever since no message was sent to the caller.
you can test with this
Request1= {"http://www.google.com",[{"cookie",undefined}, {"test",123}],"application/x-www-form-urlencoded; charset=utf-8", <<"">>}.
httpc:request(post, Request1, [{timeout,1000}], []).

Erlang cast message to global gen_server

it's continue of previos question
I have gen_server:
start(UserName) ->
case gen_server:start({global, UserName}, player, [], []) of
{ok, _} ->
io:format("Player: " ++ UserName ++ " started");
{error, Error} ->
Error
end
...
How correctly send message to this gen_server. For example: in another file i make:
gen_server:cast(test, message).
In my gen_server file i have:
handle_cast(message, State) ->
io:format("Message receiving \r\n"),
{noreply, State};
I start my gen_server with test name:
server:start(test).
test started
when i call gen_server:cast(test, message). it is nothing output in shell. How can i check handle_cast calling or not?
Thank you.
Instead of
gen_server:cast(test, message).
write
gen_server:cast({global, test}, message).
If you register name as {global, name} you must call it as {global, name}
If your handler is called it will print "Message receiving \r\n" in shell. You made that with io:format call.

Resources