Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Killing GenServer from inside

Tags:

elixir

Consider such simple GenServer module:

defmodule Crap do
  use GenServer

  ... #All the needed crap

  def handle_info(:kill_me_pls, state) do
    GenServer.stop(self)
    {:noreply, state}
  end

  def terminate(_, state) do
    IO.inspect "Look! I'm dead."
  end

end

And consider putting these expressions into the repl:

{:ok, pid} = Crap.start_link

send_message_to_that_pid

And this is where my conceirns start because Process.alive? pid returns true, but process is not responsive and terminate isn't being called, although if I call GenServer.stop(pid) called in repl on 'clean' process (that had not receive kill message) kills it properly. If stop called on process that received :kill_me_pls message hungs up the repl.

like image 438
Haito Avatar asked Sep 28 '16 18:09

Haito


1 Answers

The correct way to stop a GenServer "from inside" is to return a {:stop, reason, new_state} value from handle_* or {:stop, reason, reply, new_state} from handle_call. The reason your code fails is that calling GenServer.stop sends a special message to the GenServer, and since a GenServer is a single process and currently in handle_info, it cannot respond to the stop call, so your process hangs / creates a deadlock.

Here's the correct way:

def handle_info(:kill_me_pls, state) do
  {:stop, :normal, state}
end

You can read more about the possible return values, check out the text under the headings Return values in the documentation of GenServer.

Demo:

iex(1)> defmodule Crap do
...(1)>   use GenServer
...(1)>
...(1)>   def handle_info(:kill_me_pls, state) do
...(1)>     {:stop, :normal, state}
...(1)>   end
...(1)>
...(1)>   def terminate(_, state) do
...(1)>     IO.inspect "Look! I'm dead."
...(1)>   end
...(1)> end
iex(2)> {:ok, pid} = GenServer.start Crap, []
{:ok, #PID<0.98.0>}
iex(3)> send(pid, :kill_me_pls)
"Look! I'm dead."
:kill_me_pls
iex(4)> Process.alive?(pid)
false
like image 68
Dogbert Avatar answered Nov 02 '22 04:11

Dogbert