0

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}]}
quantumpotato
  • 9,637
  • 14
  • 70
  • 146

2 Answers2

1

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.

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
0

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.

Pascal
  • 13,977
  • 2
  • 24
  • 32