Can a gen_server receive messages from two different client processes? - erlang

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.

Related

gen_server , a server can’t call its own API functions?

When I read Erlang OTP Action book, I found this reminder on page 117:
With your RPC server, you can try calling any function exported from any module available on the server side, except one: your own tr_server:get_count/0. In general, a server can’t call its own API functions. Suppose you make a synchronous call to the same server from within one of the callback functions: for example, if handle_info/2 tries to use the get_count/0 API function. It will then perform a gen_server:call(...) to itself. But that request will be queued up until after the current call to handle_info/2 has finished, resulting in a circular wait—the server is deadlocked.
But I looked at the tr_server sample code :
get_count() ->
gen_server:call(?SERVER, get_count).
stop() ->
gen_server:cast(?SERVER, stop).
handle_info({tcp, Socket, RawData}, State) ->
do_rpc(Socket, RawData),
RequestCount = State#state.request_count,
{noreply, State#state{request_count = RequestCount + 1}};
......
do_rpc(Socket, RawData) ->
try
{M, F, A} = split_out_mfa(RawData),
Result = apply(M, F, A), % tr_server process -> handle_info -> do_rpc ->call & cast
gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Result]))
catch
_Class:Err ->
gen_tcp:send(Socket, io_lib:fwrite("~p~n", [Err]))
end.
I found the examples and cautions in the book inconsistent , the gen_server:call and gen_server:cast by tr_server process ownself.
Am I misinterpreting this?
Calling gen_server:cast from within the server process is fine, because it is asynchronous: it adds the message to the mailbox of the process and then continues, returning ok. Only gen_server:call has this problem, because it makes the process wait for an answer from itself.

Erlang gen_server with long-running tasks

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 a gen_server is up?

Is there a way to tell a gen_server: "supervisor has initialised all gen_servers, now you can send then messages"?
I have a worker gen_server whose job is to set up states of other gen_servers in his supervision tree. If I just start sending messages in init function of my configuration server, sometimes it gets {noproc, _}. I suppose that means that config server was to fast: he sent messages before supervisor had enough time to start all workers. I fixed that by putting timer:sleep(500) in config_server:init(), which ensures all gen_server had enough time to initialise, but this seems like a inelegant solution.
Is there a proper way to do this?
Return tuple with timeout 0 from init. Then immediately after it returns, handle_info(timeout, State) will be called. In handle_info make some call which won't return until the supervisor finishes initialization (e.g. supervisor:which_children).
info(PlayerId) ->
Pid = case global:whereis_name(util:getRegName({?MODULE, PlayerId})) of
P when is_pid(P) ->
P;
_ ->
{ok, P} = player_sup:start_child(PlayerId),
P
end,
gen_server:call(Pid, info).
This is my case to handle this issue. This worker process is triggered only when it is requested.
in function init() call gen_server:cast(init, State). message "init" will be first in message queue

Getting gen_server/gen_fsm state for debugging

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.

Allowing a gen_fsm to timeout if it receives no messages

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

Resources