1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2017. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(after_SUITE). 22 23%% Tests receive after. 24 25-include_lib("common_test/include/ct.hrl"). 26 27-export([all/0, suite/0, 28 t_after/1, receive_after/1, receive_after_big/1, 29 receive_after_errors/1, receive_var_zero/1, receive_zero/1, 30 multi_timeout/1, receive_after_32bit/1, 31 receive_after_blast/1]). 32 33%% Internal exports. 34 35-export([timeout_g/0]). 36 37suite() -> 38 [{ct_hooks,[ts_install_cth]}, 39 {timetrap, {minutes, 4}}]. 40 41all() -> 42 [t_after, receive_after, receive_after_big, 43 receive_after_errors, receive_var_zero, receive_zero, 44 multi_timeout, receive_after_32bit, receive_after_blast]. 45 46%% Tests for an old round-off error in 'receive after'." 47t_after(Config) when is_list(Config) -> 48 Frequent = spawn_link(fun frequent_process/0), 49 Period = test_server:minutes(1), 50 Before = erlang:monotonic_time(), 51 receive 52 after Period -> 53 After = erlang:monotonic_time(), 54 unlink(Frequent), 55 exit(Frequent, die), 56 report(Period, Before, After) 57 end. 58 59report(Period, Before, After) -> 60 case erlang:convert_time_unit(After - Before, native, 100*1000) / Period of 61 Percent when Percent > 100.10 -> 62 ct:fail({too_inaccurate, Percent}); 63 Percent when Percent < 100.0 -> 64 ct:fail({too_early, Percent}); 65 Percent -> 66 Comment = io_lib:format("Elapsed/expected: ~.2f %", [Percent]), 67 {comment, lists:flatten(Comment)} 68 end. 69 70frequent_process() -> 71 receive 72 after 100 -> 73 frequent_process() 74 end. 75 76%% Test that 'receive after' works (doesn't hang). 77%% The test takes 10 seconds to complete. 78receive_after(Config) when is_list(Config) -> 79 receive_after1(5000). 80 81receive_after1(1) -> 82 io:format("Testing: receive after ~p~n", [1]), 83 receive after 1 -> ok end; 84receive_after1(N) -> 85 io:format("Testing: receive after ~p~n", [N]), 86 receive after N -> receive_after1(N div 2) end. 87 88receive_after_big(Config) when is_list(Config) -> 89 %% Test that 'receive after' with a 32 bit number works. 90 receive_after_big1(16#f7654321), 91 receive_after_big2(). 92 93receive_after_big1(Timeout) -> 94 Self = self(), 95 erlang:yield(), 96 spawn(fun() -> Self ! here_is_a_message end), 97 ok = receive 98 here_is_a_message -> 99 ok 100 after Timeout -> 101 %% We test that the timeout can be set, 102 %% not that an timeout occurs after the appropriate delay 103 %% (48 days, 56 minutes, 48 seconds)! 104 timeout 105 end. 106 107receive_after_big2() -> 108 Self = self(), 109 erlang:yield(), 110 spawn(fun() -> Self ! here_is_a_message end), 111 ok = receive 112 here_is_a_message -> 113 ok 114 after 16#f7999977 -> 115 %% We only test that the timeout can be set. 116 timeout 117 end. 118 119-define(TryAfter(Timeout), 120 {'EXIT',{timeout_value,_}} = (catch receive mission -> exit(impossible) after Timeout -> ok end), 121 {'EXIT',{timeout_value,_}} = (catch receive after Timeout -> ok end), 122 try_after(Timeout)). 123 124%% Test error cases for 'receive after'. 125receive_after_errors(Config) when is_list(Config) -> 126 ?TryAfter(-1), 127 ?TryAfter(0.0), 128 ?TryAfter(3.14), 129 ?TryAfter(16#100000000), 130 ?TryAfter(392347129847294724972398472984729847129874), 131 ?TryAfter(16#3fffffffffffffff), 132 ?TryAfter(16#ffffffffffffffff), 133 ?TryAfter(-16#100000000), 134 ?TryAfter(-3891278094774921784123987129848), 135 ?TryAfter(xxx), 136 ok. 137 138try_after(Timeout) -> 139 {'EXIT',{timeout_value,_}} = (catch receive after Timeout -> ok end). 140 141%% Test 'after Z', when Z == 0. 142receive_var_zero(Config) when is_list(Config) -> 143 self() ! x, 144 self() ! y, 145 Z = zero(), 146 timeout = receive 147 z -> ok 148 after Z -> timeout 149 end, 150 timeout = receive 151 after Z -> timeout 152 end, 153 self() ! w, 154 receive 155 x -> ok; 156 Other -> 157 ct:fail({bad_message,Other}) 158 end. 159 160zero() -> 0. 161 162%% Test 'after 0'. 163receive_zero(Config) when is_list(Config) -> 164 self() ! x, 165 self() ! y, 166 timeout = receive 167 z -> ok 168 after 0 -> 169 timeout 170 end, 171 self() ! w, 172 timeout = receive 173 after 0 -> timeout 174 end, 175 receive 176 x -> ok; 177 Other -> 178 ct:fail({bad_message,Other}) 179 end. 180 181%% Test for catching invalid assertion in erl_message.c (in queue_message) 182%% This failed (dumped core) with debug-compiled emulator. 183multi_timeout(Config) when is_list(Config) -> 184 P = spawn(?MODULE, timeout_g, []), 185 P ! a, 186 P ! b, 187 receive 188 after 1000 -> ok 189 end, 190 P ! c, 191 receive 192 after 1000 -> ok 193 end, 194 P ! d, 195 ok. 196 197timeout_g() -> 198 receive 199 a -> ok 200 end, 201 receive 202 after 100000 -> ok 203 end, 204 ok. 205 206%% OTP-7493: Timeout for 32 bit numbers (such as 16#ffffFFFF) could 207%% timeout at once. 208receive_after_32bit(Config) when is_list(Config) -> 209 T = 16#ffffFFFF, 210 Pids = [spawn_link(fun() -> recv_after_32bit(I, T) end) || 211 I <- lists:seq(1, 2048)], 212 213 %% Wait two seconds for any of the processes to timeout too early. 214 receive after 2000 -> ok end, 215 216 %% Kill the processes. 217 [begin unlink(Pid), exit(Pid, kill) end || Pid <- Pids], 218 ok. 219 220recv_after_32bit(I, T) when I rem 2 =:= 0 -> 221 receive after T -> exit(timeout) end; 222recv_after_32bit(_, _) -> 223 receive after 16#ffffFFFF -> exit(timeout) end. 224 225blaster() -> 226 receive 227 {go, TimeoutTime} -> 228 Tmo = TimeoutTime - erlang:monotonic_time(millisecond), 229 receive after Tmo -> ok end 230 end. 231 232spawn_blasters(0) -> 233 []; 234spawn_blasters(N) -> 235 [spawn_monitor(fun () -> blaster() end)|spawn_blasters(N-1)]. 236 237receive_after_blast(Config) when is_list(Config) -> 238 PMs = spawn_blasters(10000), 239 TimeoutTime = erlang:monotonic_time(millisecond) + 5000, 240 lists:foreach(fun ({P, _}) -> P ! {go, TimeoutTime} end, PMs), 241 lists:foreach(fun ({P, M}) -> 242 receive 243 {'DOWN', M, process, P, normal} -> 244 ok 245 end 246 end, PMs). 247