GenServer exit :normal when adding recursion call

I have a GenServer that is working rock solid for months. Due to some improvements I’ve add a check before returning inside a function.

def get_valid_random(arg1) do
  ...
  if check_val(val2) != 3 do
    val2
  else
    get_valid_random(arg1)
  end
end

This is causing the GenServer to exit with :normal inside the reason. It shows me the error becasue the handle_info doesn’t pattern match this. Inside check_val it starts some Task.async, not sure if that can influence this behaviour.

[error] GenServer :genservername terminating
** (FunctionClauseError) no function clause matching in MyApp.GenServer.handle_info/2
    (myapp 0.9.30) lib/myapp/genserver.ex:122: MyApp.GenServer.handle_info({:EXIT, #PID<0.992.0>, :normal}, %State{...})
    (stdlib 3.13.2) gen_server.erl:680: :gen_server.try_dispatch/4
    (stdlib 3.13.2) gen_server.erl:756: :gen_server.handle_msg/6
    (stdlib 3.13.2) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
Last message: {:EXIT, #PID<0.992.0>, :normal}
State: %State{...}

It is weird to exit with :normal, I was expecting an error message why did this happen. Not sure if the reason is the way this function is called.

Manager.DataSource.get_manager(datasource).get_valid_random(arg1)

I would debug this further later today, but I’m curious if someone knows something about this behaviour.

The {:EXIT, pid, :normal} is what you’d expect to get when trapping exits (with Process.flag(:trap_exit, true) and a linked process exits. In particular, you’ll get this message if the code inside of a Task fails.

Check above this “Genserver :genservername terminating” message in the logs, there may be more information about what went wrong in the task.

2 Likes

Nothing in the logs (running via IEx). I need to check the PID, if is from this Genserver or something else (like Task.async). That should be the issue. I will reply back when I give it a go again.

EDIT: That was it, I needed to handle the exit signal from the linked process exit.

1 Like

I was just looking at this kind of thing and, unless I’ve got it wrong , process 1 will only terminate with a :normal reason due to linked process 2 exiting with :normal if

  • process 2 is the parent of process 1
  • process 1 is an OTP process (eg a GenServer)
  • process 1 is trapping exits

I’m not sure you can handle the exit signal as “If the special process is set to trap exits and if the parent process terminates, the expected behavior is to terminate with the same reason” from Erlang -- sys and proc_lib .

If I’m wrong I’d love to know as it means I’ve not got my head around all of this yet.

Process 1 in my case was only terminated because I didn’t implement the handle_info/2 for this specific case (catch :EXIT), so after I add the handle_info it continues to run as expected.

1 Like

Ah, ok. Just actually read your stacktrace properly and that makes sense. Thanks.

1 Like