1
votes

I am trying to figure out why my gen_server crashes with a timeout since i am treating all possible cases:

module(wk).
-behaviour(gen_server).
-compile(export_all).


-record(state,{
    limit,
    count=0,
    toSend
}).
start_link(ToSend,Limit)->
   gen_server:start_link(?MODULE, {ToSend,Limit}, []).



init({ToSend,Limit})->
    State=#state{toSend=ToSend,limit=Limit},
    {ok,State}.


handle_call({process,Message},From,State)->
    {reply,{processed,os:timestamp()},State};
handle_call(Message,From,State)->
    self() ! {from_call,Message},
    {noreply,State}.
handle_cast(Message,State=#state{count=C})->
    self() ! {from_cast,Message},
    {noreply,State}.

handle_info(Message,State=#state{count=C,limit=L,toSend=T})->
    io:format("inside handle_info"),
    T! {badrequest,Message},
    Ret=if C>L -> {stop,State};
           _ ->{noreply,State#state{count=C+1}}
        end,
    Ret.

As you can see this server can handles a number of limit unknown messages , and as well as cast messages. Now my problem is with the handle_call:

  • If i send a message that fits the first case its ok and it replies back
  • When i send an unknown message using gen_server:call(S,xx) for example, i get a timeout error :

    exception exit: {timeout,{gen_server,call,[<0.102.0>,33]}} in function gen_server:call/2 (gen_server.erl, line 215)

Why is my server timing out ? I can see that after handle_call it enters into handle_info but why does it crash ?

Usage:

{ok,Y}=wk:start_link(self(),3).
 gen_server:cast(Y,some_message).  % works limit times and then crashes as expected
 Y ! some_message % works limit times and then crashes as expected
 gen_server:call(Y,some_message) % gets inside handle_info , since i get the io message, then times out
3

3 Answers

2
votes

Why is my server timing out ?

From the gen_server:call() docs:

call(ServerRef, Request) -> Reply
call(ServerRef, Request, Timeout) -> Reply

Makes a synchronous call to the ServerRef of the gen_server process by sending a request and waiting until a reply arrives or a time-out occurs.
...
...

Timeout is an integer greater than zero that specifies how many milliseconds to wait for a reply, or the atom infinity to wait indefinitely. Defaults to 5000. If no reply is received within the specified time, the function call fails.

Because you chose to return {noreply...} in your handle_call() function, gen_server:call() cannot return and times out after 5 seconds. You need to manually call gen_server:reply/2 (in another process) to send a reply back to the client before the timeout.

1
votes

A few things going on here.

You are sending messages to self() from within the GenServer, which will be sent to the GenServer. Those messages need to be handled in the handle_info block, which I don't see implemented, so at this point, everything is being treated as a bad request.

Second, you are sending messages back to the caller/caster via it's Pid/Name that you hold in state, which is fine for async ops as long as you reply to the calls with something like {reply, "processing call", State}, since you used noreply your calls are timing out as it has been stated already. Usually, you wouldn't want to use a call that returns something right away, but if you need confirmation that the process got the job and is working on it it can be a good handoff of responsibility mechanism. Otherwise, if it's meant to be a syncronious call use the gen_server:reply instead of T ! Message syntax, but you'll have to treat calls and casts differently.

Good luck

0
votes

It's the client that timeout. once it makes a call to the second clause of gen_server:call/2 the client is still waiting for a response, which never being sent back.