aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rw-r--r--erts/emulator/beam/beam_emu.c32
-rw-r--r--erts/emulator/beam/erl_message.h8
-rw-r--r--erts/emulator/beam/ops.tab9
-rw-r--r--erts/emulator/test/Makefile1
-rw-r--r--erts/emulator/test/distribution_SUITE.erl11
-rw-r--r--erts/emulator/test/fun_r12_SUITE.erl1
-rw-r--r--erts/emulator/test/receive_SUITE.erl113
7 files changed, 170 insertions, 5 deletions
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 5b2f032afc..ee8ba54cb8 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -1539,6 +1539,10 @@ void process_main(void)
/*
* Skeleton for receive statement:
*
+ * recv_mark L1 Optional
+ * call make_ref/monitor Optional
+ * ...
+ * recv_set L1 Optional
* L1: <-------------------+
* <-----------+ |
* | |
@@ -1557,6 +1561,34 @@ void process_main(void)
*
*/
+ OpCase(recv_mark_f): {
+ /*
+ * Save the current position in message buffer and the
+ * the label for the loop_rec/2 instruction for the
+ * the receive statement.
+ */
+ c_p->msg.mark = (BeamInstr *) Arg(0);
+ c_p->msg.saved_last = c_p->msg.last;
+ Next(1);
+ }
+
+ OpCase(i_recv_set): {
+ /*
+ * If the mark is valid (points to the loop_rec/2
+ * instruction that follows), we know that the saved
+ * position points to the first message that could
+ * possibly be matched out.
+ *
+ * If the mark is invalid, we do nothing, meaning that
+ * we will look through all messages in the message queue.
+ */
+ if (c_p->msg.mark == (BeamInstr *) (I+1)) {
+ c_p->msg.save = c_p->msg.saved_last;
+ }
+ I++;
+ /* Fall through to the loop_rec/2 instruction */
+ }
+
/*
* Pick up the next message and place it in x(0).
* If no message, jump to a wait or wait_timeout instruction.
diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h
index 459c6363aa..489dee7b37 100644
--- a/erts/emulator/beam/erl_message.h
+++ b/erts/emulator/beam/erl_message.h
@@ -75,6 +75,13 @@ typedef struct {
ErlMessage** last; /* point to the last next pointer */
ErlMessage** save;
int len; /* queue length */
+
+ /*
+ * The following two fields are used by the recv_mark/1 and
+ * recv_set/1 instructions.
+ */
+ BeamInstr* mark; /* address to rec_loop/2 instruction */
+ ErlMessage** saved_last; /* saved last pointer */
} ErlMessageQueue;
#ifdef ERTS_SMP
@@ -137,6 +144,7 @@ do { \
(p)->msg.len--; \
if (__mp == NULL) \
(p)->msg.last = (p)->msg.save; \
+ (p)->msg.mark = 0; \
} while(0)
/* Reset message save point (after receive match) */
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 9e8ac74f40..49280a60e0 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1421,3 +1421,12 @@ i_gc_bif1 j I s I d
# R13B03
#
on_load
+
+#
+# R14A.
+#
+recv_mark f
+
+recv_set Fail | label Lbl | loop_rec Lf Reg => \
+ i_recv_set | label Lbl | loop_rec Lf Reg
+i_recv_set
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index df2faf1450..2424fedbed 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -81,6 +81,7 @@ MODULES= \
port_bif_SUITE \
process_SUITE \
pseudoknot_SUITE \
+ receive_SUITE \
ref_SUITE \
register_SUITE \
save_calls_SUITE \
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index 8f48d8a992..7c19274696 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -1,23 +1,24 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(distribution_SUITE).
+-compile(r12).
%% Tests distribution and the tcp driver.
diff --git a/erts/emulator/test/fun_r12_SUITE.erl b/erts/emulator/test/fun_r12_SUITE.erl
index f21299ba67..9262731dcb 100644
--- a/erts/emulator/test/fun_r12_SUITE.erl
+++ b/erts/emulator/test/fun_r12_SUITE.erl
@@ -18,6 +18,7 @@
%%
-module(fun_r12_SUITE).
+-compile(r12).
-export([all/1,init_per_testcase/2,fin_per_testcase/2,dist_old_release/1]).
diff --git a/erts/emulator/test/receive_SUITE.erl b/erts/emulator/test/receive_SUITE.erl
new file mode 100644
index 0000000000..40ebf2bd21
--- /dev/null
+++ b/erts/emulator/test/receive_SUITE.erl
@@ -0,0 +1,113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(receive_SUITE).
+
+%% Tests receive after.
+
+-include("test_server.hrl").
+
+-export([all/1,
+ call_with_huge_message_queue/1,receive_in_between/1]).
+
+-export([init_per_testcase/2,fin_per_testcase/2]).
+
+all(suite) ->
+ [call_with_huge_message_queue,receive_in_between].
+
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ Dog=?t:timetrap(?t:minutes(3)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Func, Config) ->
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog).
+
+call_with_huge_message_queue(Config) when is_list(Config) ->
+ ?line Pid = spawn_link(fun echo_loop/0),
+
+ ?line {Time,ok} = tc(fun() -> calls(10, Pid) end),
+
+ ?line [self() ! {msg,N} || N <- lists:seq(1, 500000)],
+ erlang:garbage_collect(),
+ ?line {NewTime,ok} = tc(fun() -> calls(10, Pid) end),
+ io:format("Time for empty message queue: ~p", [Time]),
+ io:format("Time for huge message queue: ~p", [NewTime]),
+
+ case (NewTime+1) / (Time+1) of
+ Q when Q < 10 ->
+ ok;
+ Q ->
+ io:format("Q = ~p", [Q]),
+ ?line ?t:fail()
+ end,
+ ok.
+
+calls(0, _) -> ok;
+calls(N, Pid) ->
+ {ok,{ultimate_answer,42}} = call(Pid, {ultimate_answer,42}),
+ calls(N-1, Pid).
+
+call(Pid, Msg) ->
+ Mref = erlang:monitor(process, Pid),
+ Pid ! {Mref,{self(),Msg}},
+ receive
+ {Mref, Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok, Reply};
+ {'DOWN', Mref, _, _, Reason} ->
+ exit(Reason)
+ end.
+
+receive_in_between(Config) when is_list(Config) ->
+ ?line Pid = spawn_link(fun echo_loop/0),
+ ?line [{ok,{a,b}} = call2(Pid, {a,b}) || _ <- lists:seq(1, 100000)],
+ ok.
+
+call2(Pid, Msg) ->
+ self() ! dummy,
+ Mref = erlang:monitor(process, Pid),
+ Pid ! {Mref,{self(),Msg}},
+ receive_one(),
+ receive
+ {Mref,Reply} ->
+ erlang:demonitor(Mref, [flush]),
+ {ok,Reply};
+ {'DOWN',Mref,_,_,Reason} ->
+ exit(Reason)
+ end.
+
+receive_one() ->
+ receive
+ dummy -> ok
+ end.
+
+%%%
+%%% Common helpers.
+%%%
+
+echo_loop() ->
+ receive
+ {Ref,{Pid,Msg}} ->
+ Pid ! {Ref,Msg},
+ echo_loop()
+ end.
+
+tc(Fun) ->
+ timer:tc(erlang, apply, [Fun,[]]).