bug in erl_eval?
Ulf Wiger
etxuwig@REDACTED
Tue Jun 17 18:06:01 CEST 2003
Mats Cronqvist stumbled upon the following surprise:
1> F1 = fun() -> process_flag(trap_exit,true),receive quit -> ok end end.
#Fun<erl_eval.19.280769>
2> P1 = spawn(F1).
<0.40.0>
3> F2 = fun() -> link(P1), receive quit -> exit(foo) end end.
#Fun<erl_eval.19.280769>
4> P2 = spawn(F2).
<0.44.0>
5> dbg:tracer().
{ok,<0.48.0>}
6> dbg:p(P1,[m]).
{ok,[{matched,nonode@REDACTED,1}]}
7> P2 ! quit.
quit
8> (<0.40.0>) << {'EXIT',<0.44.0>,foo}
9> process_info(P1).
[{current_function,{erl_eval,receive_clauses,4}},
{initial_call,{erlang,apply,2}},
{status,waiting},
{message_queue_len,0},
{messages,[]},
{links,[]},
{dictionary,[]},
{trap_exit,true},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.21.0>},
{heap_size,233},
{stack_size,17},
{reductions,45},
{garbage_collection,[{fullsweep_after,65535}]}]
** Where did the message go?! **
Putting the same code in a module and compiling it, the
example works as expected:
-module(funny).
-compile(export_all).
p1() ->
spawn(fun() ->
process_flag(trap_exit, true),
receive
quit ->
ok
end
end).
p2(P1) ->
spawn(fun() ->
link(P1),
receive
quit ->
exit(foo)
end
end).
Eshell V5.2.3.3 (abort with ^G)
1> P1 = funny:p1().
<0.30.0>
2> P2 = funny:p2(P1).
<0.32.0>
3> P2 ! quit.
quit
4> process_info(P1).
[{current_function,{funny,'-p1/0-fun-0-',0}},
{initial_call,{erlang,apply,2}},
{status,waiting},
{message_queue_len,1},
{messages,[{'EXIT',<0.32.0>,foo}]},
{links,[]},
{dictionary,[]},
{trap_exit,true},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.21.0>},
{heap_size,233},
{stack_size,1},
{reductions,3},
{garbage_collection,[{fullsweep_after,65535}]}]
Looking at erl_eval.erl, I think I understand what's
happening.
%%
%% receive(Clauses, Bindings, LocalFuncHandler, Messages)
%%
receive_clauses(Cs, Bs, Lf, Ms) ->
receive
Val ->
case match_clause(Cs, [Val], Bs, Lf) of
{B, Bs1} ->
merge_queue(Ms),
exprs(B, Bs1, Lf);
nomatch ->
receive_clauses(Cs, Bs, Lf, [Val|Ms])
end
end.
%%
%% receive_clauses(TimeOut, Clauses, TimeoutBody, Bindings, LocalFuncHandler)
%%
receive_clauses(T, Cs, TB, Bs, Lf, Ms) ->
{_,_} = statistics(runtime),
receive
Val ->
case match_clause(Cs, [Val], Bs, Lf) of
{B, Bs1} ->
merge_queue(Ms),
exprs(B, Bs1, Lf);
nomatch ->
{_,T1} = statistics(runtime),
if
T == infinity ->
receive_clauses(T, Cs, TB, Bs, Lf, [Val|Ms]);
T-T1 =< 0 ->
receive_clauses(0, Cs, TB, Bs, Lf, [Val|Ms]);
true ->
receive_clauses(T-T1, Cs, TB, Bs, Lf, [Val|Ms])
end
end
after T ->
merge_queue(Ms),
{B, Bs1} = TB,
exprs(B, Bs1, Lf)
end.
merge_queue(Ms) ->
send_all(recv_all(Ms), self()).
The evaluator consumes each message and matches it; if it
doesn't match, it puts the message in an accumulator and
waits for the next message. So the message is "queued", but
not visible to process_info/1.
I cannot think of a great solution to this problem. One
could match messages by polling process_info(self(),
messages), but that doesn't give much responsiveness in the
case where one has to wait for a message.
Suggestions? Should we just live with this?
/Uffe
--
Ulf Wiger, Senior Specialist,
/ / / Architecture & Design of Carrier-Class Software
/ / / Strategic Product & System Management
/ / / Ericsson AB, Connectivity and Control Nodes
More information about the erlang-questions
mailing list