gen_server:selective_receive/1

Ulf Wiger <>
Mon Jan 27 18:47:24 CET 2003


Perhaps a really bad idea, but I thought I'd through it out
there to see what responses I get...

Gen_server is great in many ways, but now and again, I
stumble upon the need to do a selective receive to see if
there is an important message already in the queue.

Traditionally, I've done this by sending all "important"
messages as plain messages, and handling them in
handle_info/2, but this is of course cheating and "bad" for
a couple of reasons. Another way to solve it is to simply do
a receive, looking for messages tagged as '$gen_call' or
'$gen_cast, but this breaks the gen_server abstraction --
and the format of these messages is not defined in the
gen_server API. Also, we would probably like to avoid the
gen_server callback looking for messages that mean something
to the gen_server module.

Now for the potentially disgusting idea -- I wrote this in
gen_server.erl:

selective_receive(Filter) ->
    {_, Messages} = process_info(self(), messages),
    Parent = get_parent(),
    select(Messages, Filter, Parent).

select([{'$gen_call',From,Request} = X|Rest], Filter, Parent) ->
    select_test({call,From,Request}, Filter, Rest, Parent);
select([{'$gen_cast',Msg} = X|Rest], Filter, Parent) ->
    select_test({cast, Msg}, Filter, Rest, Parent);
select([{'EXIT',Parent,Why}|Rest], Filter, Parent) ->
    %% ignore
    select(Rest, Filter, Parent);
select([Msg|Rest], Filter, Parent) when size(Msg) == 6,
					element(1,Msg) == system ->
    %% ignore
    select(Rest, Filter, Parent);
select([Msg|Rest], Filter, Parent) ->
    select_test({info, Msg}, Filter, Rest, Parent);
select([], _, _) ->
    false.


select_test(Msg, Filter, Rest, Parent) ->
    case Filter(Msg) of
	true ->
	    receive X -> {ok, X} end;
	false ->
	    select(Rest, Filter, Parent)
    end.



Here's an example of where I'd like to use it:

handle_cast({global_state, S1}, S) ->
   {noreply, S#state{global_state = S1}};
handle_cast({new_master, Pid}, S) when Pid == self() ->
   case gen_server:selective_receive(
           fun({cast, {global_state, _}}) -> true;
              (_) -> false
           end) of
      false ->
         {noreply, become_master(S)};
      {ok, {cast, {global_state, GS}}} ->
         {noreply, become_master(S#state{global_state = GS})}
   end.

(Of course, one would want to iterate over this function to
make sure that we get the last version of the global state,
but that doesn't necessarily bear on the idea as such.)

What do you think -- stupid? unnecessary? dangerous?
...good?

/Uffe
-- 
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson Telecom AB, ATM Multiservice Networks




More information about the erlang-questions mailing list