bug in erl_eval?

Ulf Wiger <>
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,[{,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