문제

Short copy from here:

exit(Pid, Reason) -> true

Types:

Pid = pid() Reason = term()

Sends an exit signal with exit reason Reason to the process Pid.

The following behavior apply if Reason is any term except normal or kill:

If Pid is not trapping exits, Pid itself will exit with exit reason Reason. If Pid is trapping exits, the exit signal is transformed into a message {'EXIT', From, Reason} and delivered to the message queue of Pid. From is the pid of the process which sent the exit signal. See also process_flag/2.

If Reason is the atom normal, Pid will not exit. If it is trapping exits, the exit signal is transformed into a message {'EXIT', From, normal} and delivered to its message queue.

If Reason is the atom kill, that is if exit(Pid, kill) is called, an untrappable exit signal is sent to Pid which will unconditionally exit with exit reason killed.

I am playing around with the exit/2 function and its behavior when self() is used as a Pid and normal as a 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>

Shouldn't it be the case that only a 'normal' exit message is sent to the shell process, so there is no reason to exit?

Similarly:

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

But:

8> spawn(fun() -> exit(self(), normal), receive _ -> ok end end).         
<0.43.0>
9> is_process_alive(v(8)).
false
도움이 되었습니까?

해결책 2

As your third example shows if any process does an exit(self(), normal) then it crashes while doing exit(AnotherPid, normal) does not crash the other process. I have verified it on an R15B. I personally think that this is a bug as sending exit signal normal to any process should not result in its crashing.

다른 팁

It looks like the Erlang shell (shell.erl) doesn't handle 'EXIT' messages of type normal any differently than other exit messages, which means that it sends an error and restarts the shell. If you really want to find out this you can trace the program flow using the debugger in this way:

  1. Download shell.erl
  2. Change the filename to shell2.erl
  3. Open the file and change the module name to shell2 as well. You need to do this because the compiler will complain about shell being in a sticky directory.
  4. Start the erl prompt.
  5. c(shell2, [debug_info]).
  6. debugger:start().
  7. Module -> Interpret and pick shell2.erl
  8. shell2:start().
  9. Trace away!

I think the reason can be found from the source code '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)  -> <<"* ">>.

For your first case (sending message to self()), the signal meets the 2nd situation of get_command1/5, it will give error message firstly, and spawn a new shell pid. Please pay attention to start_eval function.

For your second case (sending message to a spawn pid), Going back to your first part of post about the exit function, it is logical. Your spawned pid doesn't trap the exit message, and ignore the (normal) exit message. so the shell will receive exit message only when your spawn pid's actual exit. It will go to the 1st condition of get_command1/5. Because start_evel is not been called, shell pid will keep the same.

For your third case, I don't know why. I also think is_process_alive(v(8)) should return true.

For the third case, the key point here is that self() is the pid of spawned process not the shell's.

see code below:

Eshell V5.9  (abort with ^G)

1> self().

<0.32.0>

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

This is <0.35.0> <0.35.0>

3> is_process_alive(v(2)).

false

4>

It's all right there in the documentation that you quoted:

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

If you don't trap exits then your process will exit.

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

There is no "message" being sent about exiting if you don't trap exits. The process just dies. As does any process linked to it. This is what trap_exit is for.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top