Erlang の gen_server モジュールを学ぶ
以前の記事で (2年も前くらいの記事...)、gen_server ビヘイビアの概要を学びました。
今回は、gen_server モジュールに用意されている各関数と gen_server モジュールのコールバック関数を学びたいと思います。というか書きかけだったのに気がついて、今書き上げました。
gen_server はいくつかの関数をコールバック関数として export されていることを期待しています。
gen_server ビヘイビアの関数とコールバック関数の関係は下記のようになります。
例えば、gen_server:start_link 関数が呼ばれると、コールバックモジュール Module の init/1 関数が呼ばれるというふうに対応しています。
gen_server module Callback module ----------------- --------------- gen_server:start gen_server:start_link -----> Module:init/1 gen_server:stop -----> Module:terminate/2 gen_server:call gen_server:multi_call -----> Module:handle_call/3 gen_server:cast gen_server:abcast -----> Module:handle_cast/2 - -----> Module:handle_info/2 - -----> Module:handle_continue/2 - -----> Module:terminate/2 - -----> Module:code_change/3
コールバック関数が失敗するか、不正な値を返すと、gen_server プロセスは終了します。
gen_server はプロセスは sys モジュール に記述されているメッセージを扱うことができます。 sys モジュールは gen_server をデバッグするために使用することができます。
gen_server プロセスは、終了シグナル ( exit signals ) を自動的に捕捉 ( trap ) しないことに注意してください。コールバック関数の init 関数で明示的に process_flag(trap_exit, true) をする必要があります。
特に指定がない限り、指定された gen_server プロセスが存在しない場合、または不正な引数が指定されている場合、このモジュールの関数はすべて失敗します。
コールバック関数が timeout ではなくhibernate を指定している場合、gen_serverプロセスは休止状態になります( erlang:hibernate/3 を参照)。
これは、サーバーが長時間アイドル状態になることが予想される場合に役立ちます。 ただし、ハイバネーションは少なくとも、ハイバネートするときとアイドル状態から起きるときの 2回のガーベッジコレクションが行われます。また、サーバーが忙しい場合、各関数の呼び出しの間にハイバネーションを実行しない方が良いことに注意してください。
gen_server プロセスが初期化直後にアクションを実行する必要がある場合、またはコールバックの実行を複数のステップに分割する必要がある場合、タイムアウトまたはハイバネーションの値の代わりに {continue, Continue} を返すことができます。この値を返した直後に handle_continue/2 コールバック関数を呼び出します。
gen_server の関数
abcast/2, abcast/3 関数
abcast(Name, Request) -> abcast abcast(Nodes, Name, Request) -> abcast
Types
Nodes = [Node] Node = atom() Name = atom() Request = term()
この関数は、指定した Nodes のローカルに Name として登録された gen_server プロセスへ非同期リクエストを送信します。
この関数はすぐに処理が戻り、存在しないノード、または Name という名前の gen_server が存在しない場合、無視します。
gen_server プロセスは、Module:handle_cast/2 を呼び出してリクエストを処理します。
call/2, call/3 関数
call(ServerRef, Request) -> Reply call(ServerRef, Request, Timeout) -> Reply
Types
ServerRef = Name | {Name,Node} | {global,GlobalName}
| {via,Module,ViaName} | pid()
Node = atom()
GlobalName = ViaName = term()
Request = term()
Timeout = int()>0 | infinity
Reply = term()
この関数は、gen_server プロセスの ServerRef へ同期呼び出しをします。リクエストを送信し、応答が到着するかタイムアウトが発生するまで待ちます。
gen_serverプロセスは、Module:handle_call/3 を呼び出してリクエストを処理します。
ServerRef は次のいずれかです。
pid()、 gen_server プロセスのIDそのものName、gen_server プロセスがローカルに登録されている場合{Name,Node}、gen_server プロセスが別のノード (Node) のローカルに登録されている場合{global,GlobalName}、gen_server プロセスがグローバルに登録されている場合{via,Module,ViaName}、 gen_server プロセスが代替プロセスレジストリを介して登録されている場合
Request は任意の term() で Module:handle_call/3 へ渡される引数の1つです。
Timeout は応答を待機する時間(ミリ秒)、または無期限に待機することを意味するアトム infinity です。デフォルトは 5000 です。
指定された時間内に応答が受信されない場合、関数呼び出しは失敗します。
呼び出し元がタイムアウトによる失敗を catch して実行を継続している場合、サーバーは少し遅れて応答すると、呼び出し元のメッセージキューにサーバーから応答が届きます。この場合、呼び出し元は、このようなガベージメッセージを破棄する必要があります。
Reply は、Module:handle_call/3 の返り値で定義されます。
呼び出しは、タイムアウトや呼び出された gen_server プロセスが呼び出し前や呼び出し中に死ぬなど、様々な理由で失敗する可能性があります。
cast/2 関数
cast(ServerRef, Request) -> ok
Types
ServerRef = Name | {Name,Node} | {global,GlobalName}
| {via,Module,ViaName} | pid()
Node = atom()
GlobalName = ViaName = term()
Request = term()
非同期リクエストを gen_server プロセスの ServerRef に送信し、すぐに ok を返します。宛先ノードまたは gen_server プロセスが存在しない場合は無視します。
gen_server プロセスは、 Module:handle_cast/2 を呼び出してリクエストを処理します。
Request は Module:handle_cast/2 への引数の1つとして渡される term() です。
enter_loop/3, enter_loop/4, enter_loop/5 関数
enter_loop(Module, Options, State) enter_loop(Module, Options, State, ServerName) enter_loop(Module, Options, State, Timeout) enter_loop(Module, Options, State, ServerName, Timeout)
Types
Module = atom()
Options = [Option]
Option = {debug,Dbgs} | {hibernate_after,HibernateAfterTimeout}
Dbgs = [Dbg]
Dbg = trace | log | statistics
| {log_to_file,FileName} | {install,{Func,FuncState}}
State = term()
ServerName = {local,Name} | {global,GlobalName}
| {via,Module,ViaName}
Name = atom()
GlobalName = ViaName = term()
Timeout = int() | infinity
既存のプロセスを gen_server プロセスにします。戻りません。代わりに、呼び出しプロセスは gen_server プロセスの受信ループに入り、gen_server プロセスになります。
プロセスは、proc_lib の start 関数の1つを使用して開始されている必要があります。
ユーザーは、プロセスの名前の登録など、プロセスの初期化に責任があります。 この関数は、gen_server ビヘイビアが提供するよりも複雑な初期化手順が必要な場合に役立ちます。
Module、 Options および ServerNameは、start/3,4 または start_link/3,4 を呼び出すときのものと同じです。ただし、ServerNameが指定されている場合、この関数が呼び出される前にプロセスがそれに応じて登録されている必要があります。
State と Timeoutは、Module:init/1 の返り値と同じ意味を持ちます。コールバックモジュール Module は、init/1 関数をエクスポートする必要はありません。
呼び出しプロセスが proc_lib の start 関数によって開始されなかった場合、または ServerName として登録されていない場合、関数は失敗します。
multi_call/2, multi_call/3, multi_call/4 関数
multi_call(Name, Request) -> Result multi_call(Nodes, Name, Request) -> Result multi_call(Nodes, Name, Request, Timeout) -> Result
Types
Nodes = [Node]
Node = atom()
Name = atom()
Request = term()
Timeout = int()>=0 | infinity
Result = {Replies,BadNodes}
Replies = [{Node,Reply}]
Reply = term()
BadNodes = [Node]
最初にすべてのノードにリクエストを送信してから応答を待機することにより、指定されたノードで Name としてローカルに登録されたすべての gen_server プロセスに対して同期呼び出しを行います。
gen_serverプロセスは、Module:handle_call/3 を呼び出してリクエストを処理します。
この関数はタプル {Replies, BadNodes} を返します。Replies は {Node, Reply} のリストで、BadNodes は存在しなかったノード、または Name として登録された gen_server が存在しないか、gen_server から応答しなかったノードのリストです。
Nodes は、リクエストの送信先となるノード名のリストです。デフォルト値は、既知のすべてのノードのリスト [node() | nodes()] です。
Name は、各ノードのローカルに登録された gen_server プロセスの名前です。
Request は、Module:handle_call/3 への引数の1つとして渡される term() です。
Timeout は応答を待機する時間(ミリ秒)、または無期限に待機することを意味するアトム infinity です。デフォルトは infinity です。
指定された時間内にノードから応答が受信されない場合、ノードは BadNodes に追加されます。
ノード Node の gen_server プロセスから応答 Reply を受信すると、{Node, Reply} が Replies に追加されます。Reply は、Module:handle_call/3 の返り値で定義されます。
例えば、(C または Java ノードなど)ノードの1つがモニターを処理できず、要求の送信時に直ぐに gen_server プロセスが開始されないが、2秒以内には開始されるような場合、この関数はタイムアウトになるまで待機します。
タイムアウト後の遅い応答が呼び出し元のメッセージキューを汚染するのを防ぐために、仲介者プロセスを使用して呼び出しを行います。終了したプロセスに到着した後の遅い回答は破棄されます。
reply/2 関数
reply(Client, Reply) -> Result
Types
Client - see below Reply = term() Result = term()
gen_server プロセスは、Module:handle_call/3 の返り値で返信を定義できない場合に、この関数を使用して call/2,3 または multi_call/2.3.4 を呼び出したクライアントへ明示的に返信することができます。
Client は、コールバック関数に与えられる From 引数でなければなりません。Replyは、call/2,3 または multi_call/2,3,4 の返り値としてクライアントに返される任意の term() です。
start/3, start/4 関数
start(Module, Args, Options) -> Result start(ServerName, Module, Args, Options) -> Result
Types
ServerName = {local,Name} | {global,GlobalName}
| {via,Module,ViaName}
Name = atom()
GlobalName = ViaName = term()
Module = atom()
Args = term()
Options = [Option]
Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}
Dbgs = [Dbg]
Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}
SOpts = [term()]
Result = {ok,Pid} | ignore | {error,Error}
Pid = pid()
Error = {already_started,Pid} | term()
スタンドアロンの gen_serve rプロセス、つまり、監視ツリーの一部ではないため、スーパーバイザーを持たない gen_server プロセスを作成します。 引数と返り値の説明については、start_link/3,4 を参照してください。
start_link/3, 4 関数
start_link(Module, Args, Options) -> Result start_link(ServerName, Module, Args, Options) -> Result
Types
ServerName = {local,Name} | {global,GlobalName}
| {via,Module,ViaName}
Name = atom()
GlobalName = ViaName = term()
Module = atom()
Args = term()
Options = [Option]
Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}
Dbgs = [Dbg]
Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}
SOpts = [term()]
Result = {ok,Pid} | ignore | {error,Error}
Pid = pid()
Error = {already_started,Pid} | term()
監視ツリーの一部として gen_server プロセスを作成します。この関数は、スーパーバイザーによって直接または間接的に呼び出されます。gen_server プロセスがスーパーバイザーにリンクされることを保証します。
gen_server プロセスは Module:init/1 を呼び出して初期化します。同期された起動手順を確実にするために、start_link/3,4 は Module:init/1 から処理が戻るまで、この関数も処理が戻りません。
ServerName = {local,Name}の場合、gen_server プロセスはregister/2を使用して名前Nameとしてローカルに登録されます。ServerName = {global,GlobalName}の場合、gen_server プロセスID はglobal:register_name/2を使用して名前GlobalNameとしてグローバルに登録されます。ServerName = {via, Module, ViaName}の場合、gen_server プロセスはModuleで表されるレジストリに登録します。そのModuleではコールバック関数として、global モジュールの同じ名前の関数に対応するように動作する関数register_name/2、unregister_name/1、whereis_name/1、send/2をエクスポートします。つまり、これは{via, global, GlobalName}も有効な参照になることを意味しています。
Module はコールバックモジュールの名前です。
Args は Module:init/1 への引数として渡される任意の term() です。
- オプション
{timeout, Time}が存在する場合、gen_server プロセスはTimeミリ秒を初期化に費やすことが許可されるか、初期化が終わらない場合 gen_server プロセスは終了され start_link 関数は{error, timeout}を返します。 - オプション
{hibernate_after, HibernateAfterTimeout}が存在する場合、gen_server プロセスはメッセージをHibernateAfterTimeout ミリ秒待機し、メッセージが受信されない場合、proc_lib:hibernate/3を呼び出してプロセスは自動的に休止状態に入ります。 - オプション
{debug, Dbgs}が存在する場合、対応する sys 関数がDbgsの各アイテムに対して呼び出されます。sys モジュールを参照してください。 - オプション
{spawn_opt, SOpts}が存在する場合、SOptsはオプションリストとしてspawn_optBIFに渡されます。これは、gen_server プロセスの生成に使用されます。 spawn_opt/2を参照してください。
gen_server プロセスが正常に作成および初期化されると、関数は {ok, Pid} を返します。Pid は gen_server プロセスの pid です。
指定された ServerName のプロセスが既に存在する場合、関数は {error, {already_started, Pid}} を返します。Pid はそのプロセスの pid です。
Module.:init /1 が Reason で失敗した場合、関数は {error, Reason} を返します。 Module:init /1 が {stop, Reason} または ignore を返す場合、プロセスは終了し、関数はそれぞれ {error, Reason} または ignore を返します。
stop/1, stop/3 関数
stop(ServerRef) -> ok stop(ServerRef, Reason, Timeout) -> ok
Types
ServerRef = Name | {Name,Node} | {global,GlobalName}
| {via,Module,ViaName} | pid()
Node = atom()
GlobalName = ViaName = term()
Reason = term()
Timeout = int()>0 | infinity
指定された Reason で終了するように gen_server に命令し、終了するのを待ちます。 gen_server プロセスは、終了する前に Module:terminate/2 を呼び出します。
サーバーが予期した理由で終了した場合、関数は ok を返します。normal、 shutdown または {shutdown,Term} 以外の理由により終了した場合、logger を使用してエラーレポートが発行されます。デフォルトの Reason は normal です。
Timeout はゼロより大きい整数で、サーバーの終了を待機する時間(ミリ秒)、または無期限に待機することを意味するアトム infinity を指定します。デフォルトは infinity です。
指定した時間内に gen_server が終了しない場合は、timeout 例外が発生します。
プロセスが存在しない場合、noproc 例外が発生します。
コールバック関数
下記の関数は、gen_server コールバックモジュールからエクスポートされる関数です。
Module:code_change/3 関数
Module:code_change(OldVsn, State, Extra) -> {ok, NewState} | {error, Reason}
Types
OldVsn = Vsn | {down, Vsn}
Vsn = term()
State = NewState = term()
Extra = term()
Reason = term()
このコールバックはオプションであるため、コールバックモジュールでエクスポートする必要はありません。
code_change/3 が実装されていないときに appup ファイルで Change = {advanced, Extra} が指定されたリリースアップグレード/ダウングレードが行われると、undef 終了理由でプロセスがクラッシュします。
この関数は、リリースのアップグレード/ダウングレード中に内部状態を更新する場合、つまり、{update, Module, Change, ...} 命令の場合に、gen_serverプロセスによって呼び出されます。
ここで、Change = {advanced, Extra} が appupファイルで指定されます。
詳細については、OTP Design Principles の Release Handling Instructions を参照してください。
アップグレードの場合、OldVsn は Vsn です。ダウングレードの場合、OldVsn は {down, Vsn} です。 Vsn は、コールバックモジュール Module の古いバージョンの vsn 属性によって定義されます。
そのような属性が定義されていない場合、バージョンは Beam ファイルのチェックサムです。
State は gen_sever プロセスの内部状態です。
Extra はアップデート命令の {advanced, Extra} 部分から「そのまま」渡されます。
成功した場合、関数は更新された内部状態を返す必要があります。
関数が {error, Reason} を返す場合、進行中のアップグレードは失敗し、古いリリースにロールバックします。
Module:format_status/2 関数
Module:format_status(Opt, [PDict, State]) -> Status
Types
Opt = normal | terminate
PDict = [{Key, Value}]
State = term()
Status = term()
このコールバックはオプションであるため、コールバックモジュールでエクスポートする必要はありません。
デフォルトで gen_server モジュールは、コールバックモジュールの状態を返すように実装されています。
この関数は、下記の状況で gen_server プロセスによって呼び出されます。
* sys:get_status/1,2 の1つが呼び出され、gen_server ステータスを取得します。 Opt は、normal の atom が設定されます。
* gen_server プロセスは異常終了し、エラーを記録します。Opt は terminate の atom が設定されます。
この関数は、これらのケースの gen_server ステータスの形と見た目を変更するのに役立ちます。
sys:get_status/1,2 の返り値とそのステータスが終了エラーログにどのように表示されるかを変更するコールバックモジュールは、gen_serverプロセスの現在のステータスを説明する term を返す format_status/2 のインスタンスをエクスポートします。
PDict は gen_server プロセスのプロセス辞書の現在の値です。
State は gen_server プロセスの内部状態です。
この関数は現在の状態の詳細と gen_server プロセスの状態を変更する term である Status を返します。
Status が取ることのできる形式には制限はありませんが、sys:get_status/1,2 の場合(Opt が normal の場合)、Status の推奨形式は [{data, [{"State", Term}]}] です。ここで、Term は gen_server の状態に関連する詳細を提供します。
この推奨事項に従うことは必須ではありませんが、これは sys:gen_status/1,2 の返り値の残りの部分とコールバックモジュールのステータスを一致させます。
この関数の1つの用途は、状態の代替表現をコンパクトにし、大きな状態の term をログファイルに書き出すことを避けます。
Module:handle_call/3 関数
Module:handle_call(Request, From, State) -> Result
Types
Request = term()
From = {pid(),Tag}
State = term()
Result = {reply,Reply,NewState} | {reply,Reply,NewState,Timeout}
| {reply,Reply,NewState,hibernate}
| {reply,Reply,NewState,{continue,Continue}}
| {noreply,NewState} | {noreply,NewState,Timeout}
| {noreply,NewState,hibernate}
| {noreply,NewState,{continue,Continue}}
| {stop,Reason,Reply,NewState} | {stop,Reason,NewState}
Reply = term()
NewState = term()
Timeout = int()>=0 | infinity
Continue = term()
Reason = term()
gen_server プロセスが call/2,3 または multi_call/2,3,4 を使用して送信されたリクエストを受信するたびに、リクエストを処理するためにこの関数が呼び出されます。
Request は、call または multi_call に与えられる引数 Request です。
From はタプル {Pid, Tag} です。Pid はクライアントの pid、Tag は一意のタグです。
State は gen_sever プロセスの内部状態です。
{reply,Reply,NewState}、{reply,Reply,NewState,Timeout}または{reply,Reply,NewState,hibernate}を返した場合、Replyはcall/2,3の返り値としてFromに返されるか、multi_call/2,3,4の返り値に含まれます。gen_server プロセスは、新しい内部状態NewStateで実行を続けます。Timeoutとhibernateは、Module:init/1を参照してください。{noreply,NewState}または{noreply,NewState,Timeout}または{noreply,NewState,hibernate}を返した場合、gen_server プロセスはNewStateで実行を続けます。Fromへは返信されません。Fromへの返信は、reply/2を使用して明示的に指定する必要があります。{stop,Reason,Reply,NewState}を返した場合、ReplyはFromへ返されます。{stop,Reason,NewState}を返した場合、Fromへは返信されません。Fromへの返信は、reply/2を使用して明示的に指定する必要があります。gen_server プロセスはModule:terminate(Reason,NewState)を呼び出し、終了します。
Module:handle_cast/2 関数
Module:handle_cast(Request, State) -> Result
Types
Request = term()
State = term()
Result = {noreply,NewState} | {noreply,NewState,Timeout}
| {noreply,NewState,hibernate}
| {noreply,NewState,{continue,Continue}}
| {stop,Reason,NewState}
NewState = term()
Timeout = int()>=0 | infinity
Continue = term()
Reason = term()
gen_server プロセスが cast/2 または abcast/2,3 を使用して送信されたリクエストを受信するたびに、この関数はリクエストを処理するために呼び出されます。
引数と返り値の説明については、Module:handle_call/3 を参照してください。
Module:handle_continue/2 関数
Module:handle_continue(Continue, State) -> Result
Types
Continue = term()
State = term()
Result = {noreply,NewState} | {noreply,NewState,Timeout}
| {noreply,NewState,hibernate}
| {noreply,NewState,{continue,Continue}}
| {stop,Reason,NewState}
NewState = term()
Timeout = int()>=0 | infinity
Continue = term()
Reason = normal | term()
このコールバックはオプションであるため、コールバックモジュールは、別のコールバックから {continue, Continue} を返す場合にのみエクスポートする必要があります。もし continue が使用され、コールバックが実装されていない場合、プロセスは undef エラーで終了します。
この関数は、前のコールバック関数が {continue, Continue} を返すたびに gen_server プロセスによって呼び出されます。
handle_continue/2 は、前のコールバック関数の直後に呼び出されるため、初期化後に作業を実行したり、コールバック内の作業を複数のステップに分割したり、途中でプロセス状態を更新したりするのに役立ちます。
他の引数と返り値の説明については、Module:handle_call/3 を参照してください。
Module:handle_info/2 関数
Module:handle_info(Info, State) -> Result
Types
Info = timeout | term()
State = term()
Result = {noreply,NewState} | {noreply,NewState,Timeout}
| {noreply,NewState,hibernate}
| {noreply,NewState,{continue,Continue}}
| {stop,Reason,NewState}
NewState = term()
Timeout = int()>=0 | infinity
Reason = normal | term()
このコールバックはオプションであるため、コールバックモジュールでエクスポートする必要はありません。 gen_server モジュールは、この関数のデフォルトの実装を提供します。デフォルト実装は、予期しない Info メッセージについてログに記録し、ドロップして {noreply, State} を返します。
この関数は、タイムアウトが発生したとき、または同期または非同期リクエスト(またはシステムメッセージ)以外のメッセージを受信したときに、gen_server プロセスによって呼び出されます。
Infoは、タイムアウトが発生した場合を表すアトムの timeout 、または受信したメッセージのいずれかです。
Module:init/1 関数
Module:init(Args) -> Result
Types
Args = term()
Result = {ok,State} | {ok,State,Timeout} | {ok,State,hibernate}
| {ok,State,{continue,Continue}} | {stop,Reason} | ignore
State = term()
Timeout = int()>=0 | infinity
Reason = term()
gen_server プロセスが start/3,4 または start_link/3,4 を使用して開始されるたびに、この関数は新しい gen_server プロセスによって呼び出されて初期化を行います。
Args は start 関数の引数の Args です。
初期化が成功した場合、この関数は {ok, State} 、{ok, State, Timeout}、または {ok, State, hibernate} を返します。State はgen_server プロセスの内部状態です。
整数のタイムアウト値を指定すると、Timeout ミリ秒以内に要求またはメッセージが受信されない限り、タイムアウトが発生します。タイムアウトが発生すると、Module:handle_info/2 コールバック関数にアトムの timeout が渡されます。
無期限に待機させたい場合は、タイムアウト値にはアトムの infinity を使用することができます。infinity はデフォルト値です。
タイムアウト値の代わりに hibernate が指定されている場合、プロセスは次のメッセージの到着を待機するときに (proc_lib:hibernate/3 を呼び出すことで) 休止状態に入ります。
初期化に失敗した場合、この関数は {stop, Reason} を返します。Reason は任意の term か、ignore です。
Module:terminate/2 関数
Module:terminate(Reason, State)
Types
Reason = normal | shutdown | {shutdown,term()} | term()
State = term()
このコールバックはオプションであるため、コールバックモジュールでエクスポートする必要はありません。 gen_server モジュールは、クリーンアップなしでデフォルトの実装を提供します。
この関数は、 gen_server プロセスが終了しようとしているときに呼び出されます。Module:init/1関数の反対に必要な後始末を行います。この関数から処理が戻ると gen_server プロセスは Reason で終了します。返り値は無視されます。
Reason は停止理由を示す term であり、State は gen_server プロセスの内部状態です。
Reason は gen_server プロセスが終了する理由に依存します。別のコールバック関数が停止タプル {stop, ..} を返したためである場合、Reason はそのタプルで指定された値を持ちます。失敗が原因である場合は、Reason はエラーの理由です。
gen_server プロセスが監視ツリーの一部であり、スーパーバイザーによって終了するように命じられている場合、次の条件がアタハマる場合、この関数は Reason = shutdown で呼び出されます。
* gen_server プロセスは、終了シグナルをトラップするように設定されている
* スーパーバイザーの子仕様で定義されているシャットダウン戦略は、brutal_kill ではなく整数のタイムアウト値である
gen_server プロセスが監視ツリーの一部ではない場合でも、親から「EXIT」メッセージを受信すると、この関数が呼び出されます。Reason は、「EXIT」メッセージの場合と同じです。
それ以外の場合、gen_server プロセスはすぐに終了します。
normal, shutdown または {shutdown, Term} 以外の理由の場合、gen_server プロセスはエラーが原因で終了すると想定されているので、logger を使用してエラーレポートが発行されることに注意してください。