This is what the official documentation says about the terminate/2
callback for a gen_server:
This function is called by a gen_server when it is about to terminate. It should be the opposite of Module:init/1 and do any necessary cleaning up. When it returns, the gen_server terminates with Reason. The return value is ignored.
Reason is a term denoting the stop reason and State is the internal state of the gen_server.
Reason depends on why the gen_server is terminating. If it is because another callback function has returned a stop tuple {stop,..}, Reason will have the value specified in that tuple. If it is due to a failure, Reason is the error reason.
If the gen_server is part of a supervision tree and is ordered by its supervisor to terminate, this function will be called with Reason=shutdown if the following conditions apply:
- the gen_server has been set to trap exit signals, and
- the shutdown strategy as defined in the supervisor's child specification is an integer timeout value, not brutal_kill.
Even if the gen_server is not part of a supervision tree, this function will be called if it receives an 'EXIT' message from its parent. Reason will be the same as in the 'EXIT' message.
Otherwise, the gen_server will be immediately terminated.
Note that for any other reason than normal, shutdown, or {shutdown,Term} the gen_server is assumed to terminate due to an error and an error report is issued using error_logger:format/2.
What conditions cause this callback to get executed?
Assuming you have a recent Erlang available on your system (I used 18.3.3 on OS X for these tests) start it and then compile and load the code.
1> c(t).
{ok, t}
2> {ok, Pid} = t:start_link().
{ok,<0.39.0>}
3> Pid ! foo.
Got foo
foo
4> Pid ! die.
Got die
started terminate because die; sleeping for 1 second
die
finished sleeping
=ERROR REPORT==== 22-Jun-2016::13:59:23 ===
** Generic server <0.112.0> terminating
** Last message in was die
** When Server state == {state}
** Reason for termination ==
** die
These could be caused by syntax errors or other unexpected behaviors. Provoke this case by sending the crash
message to the gen_server Pid.
If you're using the {stop, Reason, State}
return value for a handle_info, handle_cast, handle_call implementation, terminate/2 will be called. Provoke this in your test gen_server using die
message.
If the parent of the gen_server sends an 'EXIT'
message, terminate will be called. Test this by sending Pid ! {'EXIT', self(), test}
to a the test gen_server. This is the same message that's sent when a process is told to trap exits sent by its parent.
Using the process exit signaling mechanism - will this cause a gen_server to execute terminate/2
? No it will not. You can test this by using the exit/2
command to send exit(Pid, kill)
to brutally kill the test gen_server. In this case, terminate/2
will not be executed.
You can also send other exit reasons like qux
or hoge
. If you do this and the process is set to trap exits (see line 26) above, then terminate/2 will be called.
That depends on if the gen_server is part of a supervision tree and if the gen_server that's being shut down is configured to trap exit signals. Remember that the special kill
signal cannot be trapped and is immediate in all cases.
If it is part of a supervision tree and the supervisor's shutdown specification is brutal_kill
, the process will be killed immediately using exit(Pid, kill)
which is untrappable and immediate as noted above.
If the shutdown specification is an integer, the supervisor will wait for that number of milliseconds before sending a kill signal to its children. If the terminate block runs longer than the shutdown timer in the supervisor's child spec, then it will not complete.
If the terminate block is called for some other reason it should run to completion.
1> c(t_sup).
{ok,t_sup}
2> c(t).
{ok,t}
3> {ok, SupPid} = t_sup:start_link().
{ok,<0.44.0>}
4> [{_,TestPid,_,_}] = supervisor:which_children(SupPid).
[{t,<0.45.0>,worker,[t]}]
5> TestPid ! bar.
Got bar
bar
6> supervisor:terminate_child(SupPid, t).
started terminate because shutdown; sleeping for 10 seconds
ok
Notice there is no finished sleeping
output as you would expect from line 52 because the supervisor sent the kill
signal after 5000 milliseconds, right in the middle of our sleep operation.
You must be one of those turtles all the down types. If terminate crashes, then it is logged to the error_logger and the process dies, as usual.
You can test this by sending the exit(Pid, daleks)
message to the test gen_server.