Неконтролируемый gen_server не вызывает завершение при получении сигнала выхода

gen_server документация по обратному вызову Module:terminate говорит:

Даже если процесс gen_server не является частью дерева надзора, эта функция вызывается, если она получает сообщение EXIT от своего родителя. Причина та же, что и в сообщении «ВЫХОД».

Вот мои handle_info и terminate функции:

handle_info(UnknownMessage, State) ->
    io:format("Got unknown message: ~p~n", [UnknownMessage]),
    {noreply, State}.

terminate(Reason, State) ->
    io:format("Terminating with reason: ~p~n", [Reason]).

Я запускаю этот сервер с помощью gen_server:start. Я предполагаю, что когда я вызываю erlang:exit(Pid, fuckoff), он должен вызывать функцию обратного вызова terminate. Но это показывает:

Got unknown message: {'EXIT',<0.33.0>,fuckoff}

Это означает, что он звонит handle_info. Но когда я звоню gen_server:stop, все работает, как указано в документации. Звоню своему gen_server из оболочки. Не могли бы вы прояснить это?

[ОБНОВЛЕНИЕ]

Здесь исходный код функции decode_msg. внутри gen_server. Если он получает какое-либо сообщение «EXIT», он должен вызвать terminate функцию:

decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
    case Msg of
    {system, From, Req} ->
        sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
                  [Name, State, Mod, Time], Hib);
    {'EXIT', Parent, Reason} ->
        terminate(Reason, Name, Msg, Mod, State, Debug);
    _Msg when Debug =:= [] ->
        handle_msg(Msg, Parent, Name, State, Mod);
    _Msg ->
        Debug1 = sys:handle_debug(Debug, fun print_event/3,
                      Name, {in, Msg}),
        handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.

В моем случае он не вызывает функцию terminate.

[ОБНОВЛЕНИЕ]

Когда я начинаю gen_server с gen_server:start_link(), отправка сигнала выхода с использованием erlang:exit(Pid, Reason) приведет к вызову terminate функции обратного вызова, что является ожидаемым поведением. Кажется, есть разница в интерпретации сигнала выхода, связан ли процесс со своим родителем или нет.


person Majid Azimi    schedule 10.09.2016    source источник
comment
Вы установили trap_exit флаг процесса на true где-то в коде?   -  person Hamidreza Soleimani    schedule 25.09.2016
comment
да. Внутри init функции.   -  person Majid Azimi    schedule 25.09.2016


Ответы (2)


Краткий ответ:

Если вы вызываете функцию exit/2 изнутри gen_server актора, она ведет себя так, как ожидалось, в соответствии с документацией, и будет вызван обратный вызов terminate/2.

Длинный ответ:

Когда вы отправляете сообщение о выходе из оболочки, значение Parent кортежа выхода устанавливается равным идентификатору процесса оболочки, с другой стороны, когда вы запускаете оболочку формы процесса gen_server, его значение Parent устанавливается на его собственный идентификатор процесса, а не оболочку идентификатор процесса, поэтому, когда он получает сообщение о выходе, он не соответствует второму предложению блока приема в функции decode_msg/8, поэтому функция terminate/6 не вызывается, и, наконец, сопоставляется следующее предложение, которое вызывает функцию handle_msg/5.

Рекомендация:

Чтобы вызвать обратный вызов terminate/3 даже при отправке сообщения о выходе процессу gen_server, вы можете перехватить сообщение о выходе в handle_info/2, а затем вернуться со стоп-кортежем следующим образом:

init([]) ->
    process_flag(trap_exit, true),
    {ok, #state{}}.

handle_info({'EXIT', _From, Reason}, State) ->
    io:format("Got exit message: ~p~n", []),
    {stop, Reason, State}.

terminate(Reason, State) ->
    io:format("Terminating with reason: ~p~n", [Reason]),
    %% do cleanup ...
    ok.
person Hamidreza Soleimani    schedule 25.09.2016
comment
Я понял. Я вижу это в observer. Почему родительский элемент имеет собственный идентификатор процесса, когда я запускаю процесс в оболочке? По какой-то особой причине? - person Majid Azimi; 25.09.2016
comment
@MajidAzimi: на основе кода gen:get_parent/0, который отвечает за поиск родителя gen_server, ищет первый элемент ключа $ancestors в словаре процесса. Поскольку процесс gen_server является процессом OTP, который запускается с proc_lib, он должен иметь идентификатор процесса вызывающего актора в словаре $ancestors, который в данном случае является идентификатором процесса оболочки, и установить его как родительский, но он не работает должным образом. . Мне нужно больше времени, чтобы понять, ошибка это или что-то еще. - person Hamidreza Soleimani; 25.09.2016
comment
Насколько я могу проверить, когда я запускаю OTP-процесс с использованием gen_*:start, родительский pid устанавливается на сам только что созданный процесс. При запуске с gen_*:start_link устанавливается на первый элемент $ancestors. Не имеет значения, является ли фактический родитель оболочкой или другим gen_ *. В обоих случаях $ancestors содержит всех родителей до корня. gen_*:start_link использует первый элемент $ancestors для получения родительского элемента (что является ожидаемым поведением), но по какой-то причине gen_*:start использует свой собственный pid в качестве родительского. - person Majid Azimi; 25.09.2016

Когда вы запускаете свой gen_server, это простой процесс, поэтому erlang:exit/1 или erlang:exit/2 работают должным образом.

  • Если Pid не захватывает выход, Pid сам выходит с причиной выхода Reason.
  • Если Pid перехватывает выходы, сигнал выхода преобразуется в сообщение {'EXIT', From, Reason} и доставляется в очередь сообщений Pid.

Итак, в настоящее время ваш код перехватывает 'EXIT' сигнал, потому что это сообщение отправляется в почтовый ящик, как и любое другое сообщение, и соответствует шаблону подстановочного знака handle_info/2.

Если вам нужна дополнительная информация об этом, вы можете прочитать gen_server исходный код и посмотрите, как он работает. Вы также можете найти свою проблему. описано в этом коде.

person Mathieu Kerjouan    schedule 10.09.2016
comment
Позвольте мне задать вопрос так: gen_server:stop также посылает сигнал выхода. Он будет преобразован в сообщение «ВЫХОД». Как gen_server отличить это сообщение от моего сообщения "ВЫХОД" (пришедшего от exit) - person Majid Azimi; 11.09.2016
comment
gen_server:handle_info/2 в настоящее время используется для перехвата каждого сигнала (даже специального). Итак, если вы хотите отличить сигнал 'EXIT' от другого сигнала, ваше определение handle_info должно быть другим (изменение сопоставления с образцом, добавление защиты ...). Чтобы было ясно, 'EXIT' signal больше похож на соглашение, чем на стандарт в Erlang, потому что это всего лишь простое сообщение. - person Mathieu Kerjouan; 11.09.2016