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