%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-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(after_SUITE). %% Tests receive after. -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, t_after/1, receive_after/1, receive_after_big/1, receive_after_errors/1, receive_var_zero/1, receive_zero/1, multi_timeout/1, receive_after_32bit/1, receive_after_blast/1]). %% Internal exports. -export([timeout_g/0]). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 4}}]. all() -> [t_after, receive_after, receive_after_big, receive_after_errors, receive_var_zero, receive_zero, multi_timeout, receive_after_32bit, receive_after_blast]. %% Tests for an old round-off error in 'receive after'." t_after(Config) when is_list(Config) -> Frequent = spawn_link(fun frequent_process/0), Period = test_server:minutes(1), Before = erlang:monotonic_time(), receive after Period -> After = erlang:monotonic_time(), unlink(Frequent), exit(Frequent, die), report(Period, Before, After) end. report(Period, Before, After) -> case erlang:convert_time_unit(After - Before, native, 100*1000) / Period of Percent when Percent > 100.10 -> ct:fail({too_inaccurate, Percent}); Percent when Percent < 100.0 -> ct:fail({too_early, Percent}); Percent -> Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]), {comment, lists:flatten(Comment)} end. frequent_process() -> receive after 100 -> frequent_process() end. %% Test that 'receive after' works (doesn't hang). %% The test takes 10 seconds to complete. receive_after(Config) when is_list(Config) -> receive_after1(5000). receive_after1(1) -> io:format("Testing: receive after ~p~n", [1]), receive after 1 -> ok end; receive_after1(N) -> io:format("Testing: receive after ~p~n", [N]), receive after N -> receive_after1(N div 2) end. receive_after_big(Config) when is_list(Config) -> %% Test that 'receive after' with a 32 bit number works. receive_after_big1(16#f7654321), receive_after_big2(). receive_after_big1(Timeout) -> Self = self(), erlang:yield(), spawn(fun() -> Self ! here_is_a_message end), ok = receive here_is_a_message -> ok after Timeout -> %% We test that the timeout can be set, %% not that an timeout occurs after the appropriate delay %% (48 days, 56 minutes, 48 seconds)! timeout end. receive_after_big2() -> Self = self(), erlang:yield(), spawn(fun() -> Self ! here_is_a_message end), ok = receive here_is_a_message -> ok after 16#f7999977 -> %% We only test that the timeout can be set. timeout end. -define(TryAfter(Timeout), {'EXIT',{timeout_value,_}} = (catch receive mission -> exit(impossible) after Timeout -> ok end), {'EXIT',{timeout_value,_}} = (catch receive after Timeout -> ok end), try_after(Timeout)). %% Test error cases for 'receive after'. receive_after_errors(Config) when is_list(Config) -> ?TryAfter(-1), ?TryAfter(0.0), ?TryAfter(3.14), ?TryAfter(16#100000000), ?TryAfter(392347129847294724972398472984729847129874), ?TryAfter(16#3fffffffffffffff), ?TryAfter(16#ffffffffffffffff), ?TryAfter(-16#100000000), ?TryAfter(-3891278094774921784123987129848), ?TryAfter(xxx), ok. try_after(Timeout) -> {'EXIT',{timeout_value,_}} = (catch receive after Timeout -> ok end). %% Test 'after Z', when Z == 0. receive_var_zero(Config) when is_list(Config) -> self() ! x, self() ! y, Z = zero(), timeout = receive z -> ok after Z -> timeout end, timeout = receive after Z -> timeout end, self() ! w, receive x -> ok; Other -> ct:fail({bad_message,Other}) end. zero() -> 0. %% Test 'after 0'. receive_zero(Config) when is_list(Config) -> self() ! x, self() ! y, timeout = receive z -> ok after 0 -> timeout end, self() ! w, timeout = receive after 0 -> timeout end, receive x -> ok; Other -> ct:fail({bad_message,Other}) end. %% Test for catching invalid assertion in erl_message.c (in queue_message) %% This failed (dumped core) with debug-compiled emulator. multi_timeout(Config) when is_list(Config) -> P = spawn(?MODULE, timeout_g, []), P ! a, P ! b, receive after 1000 -> ok end, P ! c, receive after 1000 -> ok end, P ! d, ok. timeout_g() -> receive a -> ok end, receive after 100000 -> ok end, ok. %% OTP-7493: Timeout for 32 bit numbers (such as 16#ffffFFFF) could %% timeout at once. receive_after_32bit(Config) when is_list(Config) -> T = 16#ffffFFFF, Pids = [spawn_link(fun() -> recv_after_32bit(I, T) end) || I <- lists:seq(1, 2048)], %% Wait two seconds for any of the processes to timeout too early. receive after 2000 -> ok end, %% Kill the processes. [begin unlink(Pid), exit(Pid, kill) end || Pid <- Pids], ok. recv_after_32bit(I, T) when I rem 2 =:= 0 -> receive after T -> exit(timeout) end; recv_after_32bit(_, _) -> receive after 16#ffffFFFF -> exit(timeout) end. blaster() -> receive {go, TimeoutTime} -> Tmo = TimeoutTime - erlang:monotonic_time(millisecond), receive after Tmo -> ok end end. spawn_blasters(0) -> []; spawn_blasters(N) -> [spawn_monitor(fun () -> blaster() end)|spawn_blasters(N-1)]. receive_after_blast(Config) when is_list(Config) -> PMs = spawn_blasters(10000), TimeoutTime = erlang:monotonic_time(millisecond) + 5000, lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs), lists:foreach(fun ({P, M}) -> receive {'DOWN', M, process, P, normal} -> ok end end, PMs).