Stopping Genserver vs Process.exit

3,638 views
Skip to first unread message

Adam Trepanier

unread,
May 11, 2015, 10:18:55 AM5/11/15
to elixir-l...@googlegroups.com
Ladies/Gents,

Lets say I start up a process with Genserver behavior and make two callbacks handle_call(:stop, state) and terminate(reason, state).  The handle_call callback simply does {:stop, :normal, reply, state} and the terminate just does and IO.puts("foo") and returns :ok.

I have two questions.

1. When I do GenServer.call(pid_of_process, :stop) the terminate actually gets called and outputs foo.  However when I do Process.exit(pid_of_process, :normal) terminate does not get called.  Is Process.exit a hard kill and the callback doesn't happen?

2. What is the best practice for shutting down a GenServer process?

Thanks,

Adam

James Fish

unread,
May 11, 2015, 10:47:13 AM5/11/15
to elixir-l...@googlegroups.com
On 11 May 2015 at 15:18, Adam Trepanier <adam.tr...@gmail.com> wrote:
Ladies/Gents,

Lets say I start up a process with Genserver behavior and make two callbacks handle_call(:stop, state) and terminate(reason, state).  The handle_call callback simply does {:stop, :normal, reply, state} and the terminate just does and IO.puts("foo") and returns :ok.

I have two questions.

1. When I do GenServer.call(pid_of_process, :stop) the terminate actually gets called and outputs foo.  However when I do Process.exit(pid_of_process, :normal) terminate does not get called.  Is Process.exit a hard kill and the callback doesn't happen?
 
Process.exit/2 will stop a process abruptly unless the reason is :normal or the target pid is trapping exits (Process.flag(:trap_exit, true)) and the reason is not :kill.

When trapping exits a non-kill exit signal will be turned into the message {:EXIT, from_pid, reason} with its ordering in the message queue as if it was a normal message. OTP processes, such as GenServer, will handle this exit message if it is from their parent process and terminate gracefully, i.e. call terminate/2 and exit.

Process.exit(pid, :normal) is an antipattern and should probably never be used. A :normal exit signal does not propagate so the target pid will not be effected unless it is trapping exits. Process.exit(pid, :shutdown) is the usual choice but the exit signal will propagate to linked processes.

Process.exit(pid, :kill) will abruptly stop a pid even if it is trapping exits.


2. What is the best practice for shutting down a GenServer process?

Using GenServer.call/2,3 as you described is a very good method. However it should be noted that the reply is sent after terminate/2 but before exiting. This might mean that the process is still registered. In 18.0 there will be functions that stop OTP processes and wait for them to exit: http://www.erlang.org/documentation/doc-7.0-rc1/lib/stdlib-2.3/doc/html/gen_server.html#stop-1.

An alternative is to use Supervisor.terminate_child/2 on the supervisor of the process. This uses Process.exit(pid, :shutdown) and/or Process.exit(pid, :kill) depending on strategy in the child specification and how long it takes for the process to exit.
 

Thanks,

Adam

--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/003bf54a-1253-4966-b92e-46f71676e996%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Adam Trepanier

unread,
May 11, 2015, 11:45:10 AM5/11/15
to elixir-l...@googlegroups.com
Thanks for such a detailed response.  I appreciate it.

Robert Virding

unread,
May 12, 2015, 4:45:10 PM5/12/15
to elixir-l...@googlegroups.com


On Monday, May 11, 2015 at 4:47:13 PM UTC+2, James Fish wrote:

When trapping exits a non-kill exit signal will be turned into the message {:EXIT, from_pid, reason} with its ordering in the message queue as if it was a normal message. OTP processes, such as GenServer, will handle this exit message if it is from their parent process and terminate gracefully, i.e. call terminate/2 and exit.

Process.exit(pid, :normal) is an antipattern and should probably never be used. A :normal exit signal does not propagate so the target pid will not be effected unless it is trapping exits. Process.exit(pid, :shutdown) is the usual choice but the exit signal will propagate to linked processes.

A slight correction: the :normal signal does propagate as all signals propagate. What happens when the signal arrives is determined when it arrives and whether it is :normal, :kill or anything else and whether the receiving process is trapping. As you have described. This is why links and exit signals work in a distributed system.

Robert

James Fish

unread,
May 13, 2015, 3:58:33 AM5/13/15
to elixir-l...@googlegroups.com
--
You received this message because you are subscribed to the Google Groups "elixir-lang-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-ta...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-talk/77c9c10c-2076-4de2-9048-b1977b0d49f1%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
 
Thanks for the correction Robert. I was trying to convey that a :normal exit signal does not cause an abrupt stop and so on its own would not automatically propagate to further processes. Whereas other exit signals would propagate, assuming the target process abruptly stops, to further processes until a process traps exits. 
Reply all
Reply to author
Forward
0 new messages