%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2010-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% -module(receive_SUITE). %% Tests receive after. -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, call_with_huge_message_queue/1,receive_in_between/1, receive_opt_exception/1,receive_opt_recursion/1]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 3}}]. all() -> [call_with_huge_message_queue, receive_in_between, receive_opt_exception, receive_opt_recursion]. call_with_huge_message_queue(Config) when is_list(Config) -> Pid = spawn_link(fun echo_loop/0), _WarmUpTime = time_calls(Pid), Time = time_calls(Pid), _ = [self() ! {msg,N} || N <- lists:seq(1, 500000)], io:format("Time for empty message queue: ~p", [Time]), erlang:garbage_collect(), call_with_huge_message_queue_1(Pid, Time, 5). call_with_huge_message_queue_1(_Pid, _Time, 0) -> ct:fail(bad_ratio); call_with_huge_message_queue_1(Pid, Time, NumTries) -> HugeTime = time_calls(Pid), io:format("Time for huge message queue: ~p", [HugeTime]), case (HugeTime+1) / (Time+1) of Q when Q < 10 -> ok; Q -> io:format("Too high ratio: ~p\n", [Q]), call_with_huge_message_queue_1(Pid, Time, NumTries-1) end. %% Time a number calls. Try to avoid returning a zero time. time_calls(Pid) -> time_calls(Pid, 10). time_calls(_Pid, 0) -> 0; time_calls(Pid, NumTries) -> case timer:tc(fun() -> calls(Pid) end) of {0,ok} -> time_calls(Pid, NumTries-1); {Time,ok} -> Time end. calls(Pid) -> calls(100, Pid). 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) -> Pid = spawn_link(fun echo_loop/0), [{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. receive_opt_exception(_Config) -> Recurse = fun() -> %% Overwrite with the same mark, %% and never consume it. ThrowFun = fun() -> throw(aborted) end, aborted = (catch do_receive_opt_exception(ThrowFun)), ok end, do_receive_opt_exception(Recurse), %% Eat the second message. receive Ref when is_reference(Ref) -> ok end. do_receive_opt_exception(Disturber) -> %% Create a receive mark. Ref = make_ref(), self() ! Ref, Disturber(), receive Ref -> ok after 0 -> error(the_expected_message_was_not_there) end. receive_opt_recursion(_Config) -> Recurse = fun() -> %% Overwrite with the same mark, %% and never consume it. NoOp = fun() -> ok end, BlackHole = spawn(NoOp), expected = do_receive_opt_recursion(BlackHole, NoOp, true), ok end, do_receive_opt_recursion(self(), Recurse, false), ok. do_receive_opt_recursion(Recipient, Disturber, IsInner) -> Ref = make_ref(), Recipient ! Ref, Disturber(), receive Ref -> ok after 0 -> case IsInner of true -> expected; false -> error(the_expected_message_was_not_there) end end. %%% %%% Common helpers. %%% echo_loop() -> receive {Ref,{Pid,Msg}} -> Pid ! {Ref,Msg}, echo_loop() end.