Normally if I'd like to have an Erlang process timeout I would use the following construct:
receive
Msg -> ok; %% handle message
after 60000 ->
%% Handle timeout and exit
end.
Is there a similar mechanism in the OTP servers such as gen_fsm? I will be spawning gen_fsm's for each active session with my application, and would like to have them exit if a timeout value for inactivity is exceeded after receiving a message.
I can write my own custom process if need be, but would prefer to use a gen_fsm if possible.
I dug some more and found the answer to my own question.
There is an optional fourth argument in message handler "Result"s that you can use which is a timeout.
so:
some_fsm_state({set, Val}, State) ->
NewState = do(Val, State),
{next_state, another_fsm_state, NewState, 5000};
another_fsm_state(timeout, State) ->
handle_timeout(State).
another_fsm_state({set, Val}, State) ->
%% more code that handles this state.
Once some_fsm_state is called, it transitions to the next state of "another_fsm_state" with a timeout of 5000ms. If not new message is received within 5000ms, then another_fsm_state(timeout, State) is called.
Clever OTP programmers. :)
It should be noted that this fourth element in the Results tuple can be hibernate. Please see Erlang documentation for more information.
Erlang - Hibernate
gen_fsm docs
Related
I have a gen_server (my_gen_server.erl) that is started by another server (i.e. ejabberd)
Inside my_gen_server.erl, I start another server which handles HTTP2 calls like this:
{ok, ServerPid} = apns:connect(cert, my_first_connection).
Now, my_gen_server is receiving messages both from ejabberd and ServerPid which I handle as follows:
1. handle_info({reconnecting, ServerPid}=Msg, State) -> %% do Something
2. handle_info({connection_up, ServerPid}=Msg, State) -> %% do Something
3. handle_info(#offline_msg{...} = _Msg, State) -> %% do Something
So 1 & 2 are sent by ServerPid and 3 is sent by ejabberd. This is working but I am not sure about the correct behavior. So,
My question is:
Is this correct gen_server behavior to receive/handle messages from multiple client processes?
Please help.
Any process that has the gen_server's pid can send the gen_server a message using !, which will be handled by the gen_server's function:
handl_info()
Any process that has the gen_server's pid can call the functions:
call(GenServerPid, Msg)
cast(GenServerPid, Msg)
which will be handled by the gen_server functions:
handle_call()
handle_cast()
In elixir, there is a module called Agent, which is just a gen_server that stores State, like a counter. Multiple processes can update the counter and retrieve the current count. Of course, some process has to start the gen_server, then pass the pid to the other processes that want to update/retrieve the count.
I am trying to update my process's state on a 10 second timer.
-define(INTERVAL, 3000).
start_link() ->
gen_server:start_link(?MODULE, [], []).
action(Pid, Action) ->
gen_server:call(Pid, Action).
init([]) ->
erlang:send_after(?INTERVAL, self(), trigger),
{ok, temple:new()}.
what I want to do is call this
handle_call({fight}, _From, Temple) ->
NewTemple = temple:fight(Temple),
{reply, NewTemple, NewTemple};
So I try
handle_info(trigger, _State) ->
land:action(self(), {fight}),
erlang:send_after(?INTERVAL, self(), trigger);
but I get
=ERROR REPORT==== 4-Dec-2016::19:00:35 ===
** Generic server <0.400.0> terminating
** Last message in was trigger
** When Server state == {{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[]}}},
[]}
** Reason for termination ==
** {function_clause,[{land,terminate,
[{timeout,{gen_server,call,[<0.400.0>,{fight}]}},
{{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]}}},
[]}],
[{file,"src/land.erl"},{line,47}]}
It appears that with land:action(self(), {fight}), you're attempting to make a call to the same gen_server in which you're currently handling the trigger message. Two important facts will explain why this can't work:
A call always waits for the result to be returned.
A gen_server is a process, and a process can handle only one message at a time.
In handling the trigger message, you're saying to call back to yourself and wait for yourself to process the {fight} message. Since you're in the middle of handling the trigger message, though, you'll never get to the {fight} message. You're effectively in a deadlock with yourself. That's why you're getting a timeout.
P.S. Posting an SSCCE is far more likely to get you good answers.
The error message means that there is no terminate clause in the land server module, you should have a warning when you compile the land server module.
The terminate clause is called because a timeout occurs during the gen_server call with the parameters (Pid = <0.400.0>, Message = {fight}), which was called on line land:action(self(), {fight}),. A call to a gen_server must be completed within a maximum time, by default 5000ms, You have to reduce the time spent in the fight action.
Note that it is not a good idea to increase the server timeout since a gen_server call is blocking: during the execution of a gen_server call no new message can be handled, and in your example , it is also blocking the execution of the handle_info(trigger, _State) code.
Last point, the clause handle_info(trigger, _State) should return a tuple of the form {noreply,NewState} while the last line erlang:send_after(?INTERVAL, self(), trigger); returns a timer reference, you have to modify this line.
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}], []).
Good day,
I have a gen_server process which does some long-running state-updating tasks periodically in
handle_info:
handle_info(trigger, State) ->
NewState = some_long_running_task(),
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply, NewState}.
But when such task runs, then whole server gets unresponsive and any call to it leads to whole server crash:
my_gen_server:status().
** exception exit: {timeout,{gen_server,call,[my_gen_server,status]}}
in function gen_server:call/2
How it is possible to avoid blocking of gen_server ?
And when one call my_gen_server:status() at any time, the result should be something like:
{ok, task_active}
execute the long running task in a separate process. Let this process inform the gen_server of its progress with the task (that is if the task's progress can be tracked) OR let the process complete the task or fail but at least inform the gen_server of the results of the task.
Let the gen_server be linked with the process doing this long running task, and let the gen_server know the PID or registered name so that in case of exit signals, it can isolate the death of that important process from the Rest.
handle_info(trigger, State) ->
Pid = spawn_link(?MODULE,some_long_running_task,[State]),
NewState = save_pid(Pid,State),
{noreply, NewState};
handle_info({'EXIT',SomePid,_},State)->
case lookup_pid(State) == SomePid of
false -> %% some other process
{noreply,State};
true ->
%% our process has died
%% what do we do now ?
%% spawn another one ?
%% thats your decision to take
....
....
{noreply,State}
end;
handle_info({finished,TaskResult},State)->
.....%% update state e.t.c.
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply,NewState}.
some_long_running_task(ServerState)->
....do work
....return results
This call does not lead to a crash, but simply to an exception which can be caught:
status() ->
try gen_server:call(my_gen_server, status)
catch
exit:{timeout,_} -> {ok, task_active}
end.
However, the call will remain in the server's queue, and after it finishes handling the current message, it will send a reply message: {ServerRef, Reply}, which should be discarded by the calling process.
The only way to avoid blocking of any process in Erlang (whether gen_server or not) is not to run blocking tasks on it. So another alternative could be to run your long tasks on a different process which only talks to your server, so nobody cares that it's blocked.
Is it possible to obtain the current state of a gen_server process (presumably by sending some system message)? It could be useful when debugging.
Of course, I can add a message which returns the current state to handle_call:
get_state(Server) -> gen_server:call(Server, '$get_state').
%% in every gen_server I want to debug
...
handle_call('$get_state', _From, State) ->
{reply, State, State};
...
but is there something built-in (even if it is a bit hacky)?
Use sys:get_status/1,2 function. It's definition is:
get_status(Name,Timeout) ->
{status, Pid, {module, Mod}, [PDict, SysState, Parent, Dbg, Misc]}
SysState will contain state of the process. It works for all processes using OTP behaviors and other processes implementing proc_lib and sys requirements.
There is actually a function that returns the state directly: sys:get_state/1,2. It accepts pid or name of the process and can optionally be given a timeout.