Ошибка Erlang exit/2 в R15B03

Краткая копия из здесь:

exit(Pid, Reason) -> true

Типы:

Код = pid() Причина = term()

Отправляет сигнал выхода с причиной выхода Reason процессу Pid.

Следующее поведение применяется, если Reason является любым термином, кроме normal или kill:

Если Pid не перехватывает выходы, Pid сам выйдет с причиной выхода Reason. Если Pid перехватывает выходы, сигнал выхода преобразуется в сообщение {'EXIT', From, Reason} и доставляется в очередь сообщений Pid. From — это pid процесса, отправившего сигнал выхода. См. также process_flag/2.

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

Если Reason является атомом kill, то есть если вызывается exit(Pid, kill), на Pid отправляется неперехватываемый сигнал выхода, который безоговорочно завершает работу с причиной выхода killed.

Я играю с функцией exit/2 и ее поведением, когда self() используется как Pid, а normal как Reason.

Erlang R15B03 (erts-5.9.3) [source] [64-bit] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9.3  (abort with ^G)
1> self().
<0.32.0>
2> exit(self(), normal).
** exception exit: normal
3> self().
<0.35.0>

Разве не должно быть так, что процессу оболочки отправляется только «нормальное» сообщение о выходе, поэтому нет причин для выхода?

Так же:

4> spawn(fun() -> receive Pid -> Pid ! ok end end). 
<0.38.0>
5> exit(v(4), normal).
true
6> v(4) ! self().
<0.35.0>
7> flush().
Shell got ok
ok

Но:

8> spawn(fun() -> exit(self(), normal), receive _ -> ok end end).         
<0.43.0>
9> is_process_alive(v(8)).
false

person aronisstav    schedule 19.11.2012    source источник


Ответы (5)


Как показывает ваш третий пример, если какой-либо процесс выполняет exit(self(), normal), то происходит сбой, а выполнение exit(AnotherPid, normal) не приводит к сбою другого процесса. Я проверил это на R15B. Лично я считаю, что это ошибка, так как отправка сигнала выхода normal любому процессу не должна приводить к его падению.

person rvirding    schedule 20.11.2012

Похоже на оболочку Erlang (shell.erl) не обрабатывает сообщения 'EXIT' типа normal иначе, чем другие сообщения выхода, что означает, что он отправляет ошибку и перезапускает оболочку. Если вы действительно хотите это выяснить, вы можете проследить ход выполнения программы с помощью отладчика таким образом:

  1. Загрузите shell.erl
  2. Измените имя файла на shell2.erl
  3. Откройте файл и также измените имя модуля на shell2. Вам нужно сделать это, потому что компилятор будет жаловаться на то, что shell находится в липком каталоге.
  4. Запустите приглашение erl.
  5. c(shell2, [debug_info]).
  6. debugger:start().
  7. Module -> Interpret и выберите shell2.erl
  8. shell2:start().
  9. Отследить!
person Emil Vikström    schedule 19.11.2012

Думаю, причину можно найти в исходниках 'stdlib-1.18.2/src/shell.erl'.

get_command(Prompt, Eval, Bs, RT, Ds) ->
    Parse = fun() -> exit(io:parse_erl_exprs(Prompt)) end,
    Pid = spawn_link(Parse),
    get_command1(Pid, Eval, Bs, RT, Ds).

get_command1(Pid, Eval, Bs, RT, Ds) ->
    receive
    {'EXIT', Pid, Res} ->
        {Res, Eval};
    {'EXIT', Eval, {Reason,Stacktrace}} ->
            report_exception(error, {Reason,Stacktrace}, RT),
        get_command1(Pid, start_eval(Bs, RT, Ds), Bs, RT, Ds);
    {'EXIT', Eval, Reason} ->
            report_exception(error, {Reason,[]}, RT),
        get_command1(Pid, start_eval(Bs, RT, Ds), Bs, RT, Ds)
    end.

report_exception(Class, Reason, RT) ->
    report_exception(Class, serious, Reason, RT).

report_exception(Class, Severity, {Reason,Stacktrace}, RT) ->
    Tag = severity_tag(Severity),
    I = iolist_size(Tag) + 1,
    PF = fun(Term, I1) -> pp(Term, I1, RT) end,
    SF = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
    io:requests([{put_chars, Tag},
                 {put_chars, 
                  lib:format_exception(I, Class, Reason, Stacktrace, SF, PF)},
                 nl]).

start_eval(Bs, RT, Ds) ->
    Self = self(),
    Eval = spawn_link(fun() -> evaluator(Self, Bs, RT, Ds) end), %%<========start a new shell pid
    put(evaluator, Eval),
    Eval.

severity_tag(fatal)   -> <<"*** ">>;
severity_tag(serious) -> <<"** ">>;
severity_tag(benign)  -> <<"* ">>.

Для вашего первого случая (отправка сообщения в self()) сигнал соответствует второй ситуации get_command1/5, сначала он выдаст сообщение об ошибке и создаст новый идентификатор оболочки. Пожалуйста, обратите внимание на функцию start_eval.

Для вашего второго случая (отправка сообщения спавну pid), возвращаясь к вашей первой части поста о функции exit, это логично. Ваш порожденный pid не перехватывает сообщение о выходе и игнорирует сообщение (normal) exit. поэтому shell получит сообщение о выходе только тогда, когда ваш спавн pid действительно выйдет. Это перейдет к 1-му состоянию get_command1/5. Поскольку start_evel не вызывался, shell pid останется прежним.

Что касается вашего третьего случая, я не знаю, почему. Я также думаю, что is_process_alive(v(8)) должно возвращать true.

person Chen Yu    schedule 19.11.2012

В третьем случае ключевым моментом здесь является то, что self() — это pid порожденного процесса, а не оболочки.

см. код ниже:

Eshell V5.9  (abort with ^G)

1> себя().

<0.32.0>

2> spawn(fun() -> io:format("Это ~p~n",[self()]),exit(self(), normal), receive _ -> ok end end).

Это ‹0.35.0> ‹0.35.0>

3> is_process_alive(v(2)).

ложный

4>

person ligaoren    schedule 20.11.2012
comment
Я знаю об этом. Дело в том, что порождённый процесс посылает себе обычный сигнал выхода, который не должен приводить к его завершению. - person aronisstav; 20.11.2012

В документации, которую вы процитировали, все в порядке:

If Pid is not trapping exits, Pid itself will exit with exit reason Reason.

Если вы не перехватываете выходы, ваш процесс завершится.

1> self().
<0.32.0>
2> process_flag(trap_exit, true).
false
3> exit(self(), normal).
true
4> self().
<0.32.0>
5> flush().             
Shell got {'EXIT',<0.32.0>,normal}
ok

Нет "сообщения" о выходе, если вы не перехватываете выходы. Процесс просто умирает. Как и любой процесс, связанный с ним. Для этого предназначен trap_exit.

person Daniel Luna    schedule 28.11.2012
comment
Ой. Я не читал всю документацию: If Reason is the atom normal, Pid will not exit. - person Daniel Luna; 28.11.2012