本文主要记录各种情况下gen_server进程退出的表现。
研究动机起源于Elixir/Phoenix框架中遇到的一个进程异常退出问题。因为网络异常,客户端超过一段时间未发来消息,channel进程(一个gen_server)退出了,但是挂的很突兀,不调用 terminate,进而导致掉线处理没被调用,表现出一堆bug。分析发现它是被shutdown的,而一个gen_server如果没有trap_exit,被shutdown就不会调用terminate。
Anyway,实验了各种组合,仅以此做个记录。个中缘由有兴趣的可以阅读源码。
Process.exit
trap_exit不为true
当trap_exit不为true时,Process.exit(Pid, normal) 只有在自身调用时才会退出,其他进程中调用不会导致退出。而除normal外的其他任何一种Reason都会导致进程退出。
trap_exit=true
当trap_exit=true时,所有Reason都会导致进程退出,也就是说其他进程也能用 normal 退出该进程。
Reason=kill 时 terminate 回调不会被调用,而Reason为其他值均会调用。
Reason 不属于 normal、shutdown、kill时,还会触发gen_server的exit log。
主动退出
主动退出是指进程主动在回调中返回 {stop, Reason, State} 的情况。
这种情况下,所有Reason都会触发 terminate 回调,而除 normal 和 shutdown 外,其他Reason都会触发gen_server的exit log。
父进程退出
trap_exit不为true
当trap_exit不为true时,父进程退出相当于Process.exit(Pid, Reason) 情况。由于不是自身调用,normal不会导致子进程死亡,其他理由均会导致进程死亡,并且不会调用 terminate 回调。
trap_exit=true
当trap_exit=true时,所有Reason都会导致进程退出,并且所有Reason都会触发 terminate 回调。
除 normal 和 shutdown外,其他Reason均会触发gen_server的exit log。
子进程退出对父进程的影响
以上所有的退出方式中,只有退出理由为 normal ,才不会往父进程辐射死亡信号。
而父进程收到死亡信号,如果父进程trap_exit不等于true,则会以同一理由随之死亡并往上传递,如trap_exit=true,则变为父进程收到一条 {'EXIT', 进程pid,退出理由} 消息。