[erlang-questions] Selective receive issue
Anders Ramsell
anders@REDACTED
Mon Sep 1 09:53:46 CEST 2008
Hi!
Sorry for jumping in to an old thread like this but I believe
there is one way to approach this that hasn't been tried.
Let's start with a slight rewrite of Vlad's example:
loop(Running) ->
receive
start -> loop(true);
stop -> loop(false)
after 0 ->
if Running -> process_one_step(), loop(Running);
true -> loop(Running)
end
end.
process_one_step() ->
receive
{input, Data} -> Data
end.
The problem was that with two separate receive clauses you can't
add an anonymous "catch all" to clean up unwanted messages in
either of them because you might then throw away wanted messages
too. And since we don't know how the unwanted messages look we
can't easily pattern match on them.
But we do know how the unwanted messages *doesn't* look.
Let's add some clauses to loop throwing away messages.
loop(Running) ->
receive
start -> loop(true);
stop -> loop(false);
X when not is_tuple(X) -> loop(Running);
X when tuple_size /= 2 -> loop(Running);
{X, _} when X /= input -> loop(Running)
after 0 ->
if Running -> process_one_step(), loop(Running);
true -> loop(Running)
end
end.
process_one_step() ->
receive
{input, Data} -> Data
end.
Now loop should throw away all junk in the inbox. However in the
"real world" our messages may be quite a bit more complex and
doing this could then obscure what we really want to do.
Lets move the cleaning code to it's own function with a separate
receive.
loop(Running) ->
my_flush(),
receive
start -> loop(true);
stop -> loop(false)
after 0 ->
if Running -> process_one_step(), loop(Running);
true -> loop(Running)
end
end.
process_one_step() ->
receive
{input, Data} -> Data
end.
my_flush() ->
receive
X when is_atom(X), X /= start, X/= stop -> my_flush();
X when not is_tuple(X) -> my_flush();
X when tuple_size /= 2 -> my_flush();
{X, _} when X /= input -> my_flush()
after 0 ->
ok
end.
This forced us to add another clause or we would have flushed the
start and stop messages too. In return we have a separate
function for flushing unwanted messages which is easier to keep
up-to-date and can be called from more than one place in our
program.
As our program grows keeping my_flush correct may be hard unless
we keep wanted messages easy to identify. In my "final" version I
have changed all wanted messages to be 2-tuples starting with a
known tag.
loop(Running) ->
my_flush(),
receive
{my_tag, start} -> loop(true);
{my_tag, stop} -> loop(false)
after 0 ->
if Running -> process_one_step(), loop(Running);
true -> loop(Running)
end
end.
process_one_step() ->
receive
{my_tag, {input, Data}} -> Data
end.
my_flush() ->
receive
X when not is_tuple(X) -> my_flush();
X when tuple_size /= 2 -> my_flush();
{X, _} when X /= my_tag -> my_flush()
after 0 ->
ok
end.
If all wanted messages follow a simple principle like this then
my_flush should never have to be updated.
So just because we don't know what the junk looks like does not
have to mean we can't throw it away.
/Anders Ramsell
More information about the erlang-questions
mailing list