3

I am getting a strange error report that makes me think some calls are being executed before the gen_server initialization.

Here is the init code:

init([ResourceId]) ->
    process_flag(trap_exit, true),
    {ok, {not_initialized, ResourceId}, 0}.

Here is the handle_info that should initialize the resource.

handle_info(timeout, {not_initialized, ResourceId}) ->
    Resource = data_store:get_resource(ResourceId),
    {noreply, Resource, ?CACHE_TIMEOUT};

the return value of data_store:get_resource(ResourceId) is the #resouce{} record, used to match all the handle_call methods.

The crash report with a function clause since the process is still not initialized.

=CRASH REPORT==== 1-Feb-2013::14:20:03 ===
crasher:
  initial call: gbanga_resources:init/1
  pid: <0.11772.0>  
  registered_name: []
  exception exit: {function_clause,
                      [{gbanga_resources,terminate,
                           [{function_clause,
                                [{gbanga_resources,handle_call,
                                     [get_resource,
                                      {<0.11658.0>,#Ref<0.0.0.240914>},
                                      {not_initialized,12697711}],
                                     [{file,"src/gbanga_resources.erl"},
                                      {line,120}]},
                                 {gen_server,handle_msg,5,
                                     [{file,"gen_server.erl"},{line,588}]},
                                 {proc_lib,init_p_do_apply,3,
                                     [{file,"proc_lib.erl"},{line,227}]}]},
                            {not_initialized,12697711}],
                           [{file,"src/gbanga_resources.erl"},{line,176}]},
                       {gen_server,terminate,6,
                           [{file,"gen_server.erl"},{line,722}]},
                       {proc_lib,init_p_do_apply,3,
                           [{file,"proc_lib.erl"},{line,227}]}]}
    in function  gen_server:terminate/6 (gen_server.erl, line 725)
  ancestors: [gbanga_resources_sup,gbanga_workers_sup,<0.92.0>]
  messages: [{'$gen_call',{<0.11638.0>,#Ref<0.0.0.240915>},get_resource},
                {'$gen_call',{<0.11633.0>,#Ref<0.0.0.240916>},get_resource}]
  links: [<0.6609.0>]
  dictionary: []

This should never happens if the handle_info timeout allways is called before any handle_call.

Does anyone knows why this is happening ?

4

2 に答える 2

3

サーバーの初期化を短くするためにこれを行っていると思います。data_store:get_resource/1そうでなければ、関数を で直接呼び出さない本当の理由はありませんinit/1

これを行う「標準的な」方法は、自分自身にキャストinit/1することです。

init([ResourceId]) ->
    process_flag(trap_exit, true),
    gen_server:cast(self(), init_phase_2),                 %Must be a cast!
    {ok, {not_initialized, ResourceId}}.

handle_cast(init_phase_2, {not_initialized,ResourceId}) ->
    Resource = data_store:get_resource(ResourceId),
    {noreply, Resource}.

Pidが返されていないため、これが処理される最初のメッセージになることがわかっているため、他の誰もサーバーにメッセージを送信できません。

于 2013-02-01T17:59:21.897 に答える
3

保証?いいえ。タイムアウトは他のメッセージと同じです。init 関数の途中で別のプロセスからメッセージを受け取った場合、そのメッセージを最初に処理する可能性があります。

そうは言っても、 gen_server のようなOTP プロセスの init 関数は、呼び出しプロセスから同期されます。つまり、Pid を受け取るまでにプロセスは init 関数を終了し、別のプロセスがメッセージを送信することは非常に困難になります。タイムアウトを実行する前に。

もちろん、この動作にタイムアウトを使用することはお勧めしません。プロセスが init から戻ったときに多かれ少なかれ譲歩し、Erlang のタイマーは時間通りに起動することが保証されていないため、何が起こるかはあまり明確に定義されていません (timer:sleep(5000) は少なくとも5 秒間スリープします、正確に 5 秒ではありません)。代わりに、にメッセージを送ってくださいself()。これにより、 init が戻るにメッセージがメールボックスに届くため、処理が必要であることをプロセスにすぐに知らせることができます。

于 2013-02-01T16:05:17.087 に答える