1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1998-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: -module(timer_bif_SUITE). 21: 22: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 23: init_per_group/2,end_per_group/2, 24: init_per_testcase/2,end_per_testcase/2]). 25: -export([start_timer_1/1, send_after_1/1, send_after_2/1, send_after_3/1, 26: cancel_timer_1/1, 27: start_timer_big/1, send_after_big/1, 28: start_timer_e/1, send_after_e/1, cancel_timer_e/1, 29: read_timer_trivial/1, read_timer/1, 30: cleanup/1, evil_timers/1, registered_process/1]). 31: 32: -include_lib("test_server/include/test_server.hrl"). 33: 34: init_per_testcase(_Case, Config) -> 35: ?line Dog=test_server:timetrap(test_server:seconds(30)), 36: case catch erts_debug:get_internal_state(available_internal_state) of 37: true -> ok; 38: _ -> erts_debug:set_internal_state(available_internal_state, true) 39: end, 40: [{watchdog, Dog}|Config]. 41: 42: end_per_testcase(_Case, Config) -> 43: Dog = ?config(watchdog, Config), 44: test_server:timetrap_cancel(Dog), 45: ok. 46: 47: init_per_suite(Config) -> 48: Config. 49: 50: end_per_suite(_Config) -> 51: catch erts_debug:set_internal_state(available_internal_state, false). 52: 53: suite() -> [{ct_hooks,[ts_install_cth]}]. 54: 55: all() -> 56: [start_timer_1, send_after_1, send_after_2, 57: cancel_timer_1, start_timer_e, send_after_e, 58: cancel_timer_e, start_timer_big, send_after_big, 59: read_timer_trivial, read_timer, cleanup, evil_timers, 60: registered_process]. 61: 62: groups() -> 63: []. 64: 65: init_per_group(_GroupName, Config) -> 66: Config. 67: 68: end_per_group(_GroupName, Config) -> 69: Config. 70: 71: 72: start_timer_1(doc) -> ["Basic start_timer/3 functionality"]; 73: start_timer_1(Config) when is_list(Config) -> 74: Ref1 = erlang:start_timer(1000, self(), plopp), 75: ok = get(1100, {timeout, Ref1, plopp}), 76: 77: false = erlang:read_timer(Ref1), 78: false = erlang:cancel_timer(Ref1), 79: false = erlang:read_timer(Ref1), 80: 81: Ref2 = erlang:start_timer(1000, self(), plapp), 82: Left2 = erlang:cancel_timer(Ref2), 83: UpperLimit = 1000, 84: true = (Left2 > 900) and (Left2 =< UpperLimit), 85: empty = get_msg(), 86: false = erlang:cancel_timer(Ref2), 87: 88: Ref3 = erlang:start_timer(1000, self(), plopp), 89: no_message = get(900, {timeout, Ref3, plopp}), 90: ok. 91: 92: send_after_1(doc) -> ["Basic send_after/3 functionality"]; 93: send_after_1(Config) when is_list(Config) -> 94: ?line Ref3 = erlang:send_after(1000, self(), plipp), 95: ?line ok = get(1500, plipp), 96: ?line false = erlang:read_timer(Ref3), 97: ok. 98: 99: start_timer_big(doc) -> ["Big timeouts for start_timer/3"]; 100: start_timer_big(Config) when is_list(Config) -> 101: ?line Big = 1 bsl 31, 102: ?line R = erlang:start_timer(Big, self(), hej), 103: ?line timer:sleep(200), 104: ?line Left = erlang:cancel_timer(R), 105: ?line case Big - Left of 106: Diff when Diff >= 200, Diff < 10000 -> 107: ok; 108: _Diff -> 109: test_server:fail({big, Big, Left}) 110: end, 111: ok. 112: 113: send_after_big(doc) -> ["Big timeouts for send_after/3"]; 114: send_after_big(Config) when is_list(Config) -> 115: ?line Big = 1 bsl 31, 116: ?line R = erlang:send_after(Big, self(), hej), 117: ?line timer:sleep(200), 118: ?line Left = erlang:cancel_timer(R), 119: ?line case Big - Left of 120: Diff when Diff >= 200, Diff < 10000 -> 121: ok; 122: _Diff -> 123: test_server:fail({big, Big, Left}) 124: end, 125: ok. 126: 127: send_after_2(doc) -> ["send_after/3: messages in the right order, kind version"]; 128: send_after_2(Config) when is_list(Config) -> 129: ?line _ = erlang:send_after(5000, self(), last), 130: ?line _ = erlang:send_after(0, self(), a0), 131: ?line _ = erlang:send_after(200, self(), a2), 132: ?line _ = erlang:send_after(100, self(), a1), 133: ?line _ = erlang:send_after(500, self(), a5), 134: ?line _ = erlang:send_after(300, self(), a3), 135: ?line _ = erlang:send_after(400, self(), a4), 136: ?line [a0,a1,a2,a3,a4,a5,last] = collect(last), 137: ok. 138: 139: send_after_3(doc) -> ["send_after/3: messages in the right order, worse than send_after_2"]; 140: send_after_3(Config) when is_list(Config) -> 141: _ = erlang:send_after(100, self(), b1), 142: _ = erlang:send_after(101, self(), b2), 143: _ = erlang:send_after(102, self(), b3), 144: _ = erlang:send_after(103, self(), last), 145: [b1, b2, b3, last] = collect(last), 146: 147: % This behaviour is not guaranteed: 148: % ?line _ = erlang:send_after(100, self(), c1), 149: % ?line _ = erlang:send_after(100, self(), c2), 150: % ?line _ = erlang:send_after(100, self(), c3), 151: % ?line _ = erlang:send_after(100, self(), last), 152: % ?line [c1, c2, c3, last] = collect(last), 153: 154: ok. 155: 156: cancel_timer_1(doc) -> ["Check trivial cancel_timer/1 behaviour"]; 157: cancel_timer_1(Config) when is_list(Config) -> 158: ?line false = erlang:cancel_timer(make_ref()), 159: 160: ok. 161: 162: start_timer_e(doc) -> ["Error cases for start_timer/3"]; 163: start_timer_e(Config) when is_list(Config) -> 164: ?line {'EXIT', _} = (catch erlang:start_timer(-4, self(), hej)), 165: ?line {'EXIT', _} = (catch erlang:start_timer(4728472847827482, 166: self(), hej)), 167: 168: ?line {'EXIT', _} = (catch erlang:start_timer(4.5, self(), hej)), 169: ?line {'EXIT', _} = (catch erlang:start_timer(a, self(), hej)), 170: 171: ?line Node = start_slave(), 172: ?line Pid = spawn(Node, timer, sleep, [10000]), 173: ?line {'EXIT', _} = (catch erlang:start_timer(1000, Pid, hej)), 174: ?line stop_slave(Node), 175: 176: 177: ok. 178: 179: send_after_e(doc) -> ["Error cases for send_after/3"]; 180: send_after_e(suite) -> []; 181: send_after_e(Config) when is_list(Config) -> 182: ?line {'EXIT', _} = (catch erlang:send_after(-4, self(), hej)), 183: ?line {'EXIT', _} = (catch erlang:send_after(4728472847827482, 184: self(), hej)), 185: 186: ?line {'EXIT', _} = (catch erlang:send_after(4.5, self(), hej)), 187: ?line {'EXIT', _} = (catch erlang:send_after(a, self(), hej)), 188: 189: ?line Node = start_slave(), 190: ?line Pid = spawn(Node, timer, sleep, [10000]), 191: ?line {'EXIT', _} = (catch erlang:send_after(1000, Pid, hej)), 192: ?line stop_slave(Node), 193: ok. 194: 195: cancel_timer_e(doc) -> ["Error cases for cancel_timer/1"]; 196: cancel_timer_e(suite) -> []; 197: cancel_timer_e(Config) when is_list(Config) -> 198: ?line {'EXIT', _} = (catch erlang:cancel_timer(1)), 199: ?line {'EXIT', _} = (catch erlang:cancel_timer(self())), 200: ?line {'EXIT', _} = (catch erlang:cancel_timer(a)), 201: ok. 202: 203: read_timer_trivial(doc) -> ["Trivial and error test cases for read_timer/1."]; 204: read_timer_trivial(suite) -> []; 205: read_timer_trivial(Config) when is_list(Config) -> 206: ?line false = erlang:read_timer(make_ref()), 207: ?line {'EXIT', _} = (catch erlang:read_timer(42)), 208: ?line {'EXIT', _} = (catch erlang:read_timer(423497834744444444457667444444)), 209: ?line {'EXIT', _} = (catch erlang:read_timer(self())), 210: ?line {'EXIT', _} = (catch erlang:read_timer(ab)), 211: ok. 212: 213: read_timer(doc) -> ["Test that read_timer/1 seems to return the correct values."]; 214: read_timer(suite) -> []; 215: read_timer(Config) when is_list(Config) -> 216: ?line Big = 1 bsl 31, 217: ?line R = erlang:send_after(Big, self(), hej_hopp), 218: 219: ?line receive after 200 -> ok end, % Delay and clear reductions. 220: ?line Left = erlang:read_timer(R), 221: ?line Left = erlang:cancel_timer(R), 222: ?line false = erlang:read_timer(R), 223: 224: ?line case Big - Left of 225: Diff when Diff >= 200, Diff < 10000 -> 226: ok; 227: _Diff -> 228: test_server:fail({big, Big, Left}) 229: end, 230: ok. 231: 232: cleanup(doc) -> []; 233: cleanup(suite) -> []; 234: cleanup(Config) when is_list(Config) -> 235: ?line Mem = mem(), 236: %% Timer on dead process 237: ?line P1 = spawn(fun () -> ok end), 238: ?line wait_until(fun () -> process_is_cleaned_up(P1) end), 239: ?line T1 = erlang:start_timer(10000, P1, "hej"), 240: ?line T2 = erlang:send_after(10000, P1, "hej"), 241: ?line Mem = mem(), 242: ?line false = erlang:read_timer(T1), 243: ?line false = erlang:read_timer(T2), 244: ?line Mem = mem(), 245: %% Process dies before timeout 246: ?line P2 = spawn(fun () -> receive after 500 -> ok end end), 247: ?line T3 = erlang:start_timer(10000, P2, "hej"), 248: ?line T4 = erlang:send_after(10000, P2, "hej"), 249: ?line true = Mem < mem(), 250: ?line true = is_integer(erlang:read_timer(T3)), 251: ?line true = is_integer(erlang:read_timer(T4)), 252: ?line wait_until(fun () -> process_is_cleaned_up(P2) end), 253: ?line false = erlang:read_timer(T3), 254: ?line false = erlang:read_timer(T4), 255: ?line Mem = mem(), 256: %% Cancel timer 257: ?line P3 = spawn(fun () -> receive after 20000 -> ok end end), 258: ?line T5 = erlang:start_timer(10000, P3, "hej"), 259: ?line T6 = erlang:send_after(10000, P3, "hej"), 260: ?line true = Mem < mem(), 261: ?line true = is_integer(erlang:cancel_timer(T5)), 262: ?line true = is_integer(erlang:cancel_timer(T6)), 263: ?line false = erlang:read_timer(T5), 264: ?line false = erlang:read_timer(T6), 265: ?line exit(P3, kill), 266: ?line Mem = mem(), 267: %% Timeout 268: ?line Ref = make_ref(), 269: ?line T7 = erlang:start_timer(500, self(), Ref), 270: ?line T8 = erlang:send_after(500, self(), Ref), 271: ?line true = Mem < mem(), 272: ?line true = is_integer(erlang:read_timer(T7)), 273: ?line true = is_integer(erlang:read_timer(T8)), 274: ?line receive {timeout, T7, Ref} -> ok end, 275: ?line receive Ref -> ok end, 276: ?line Mem = mem(), 277: ?line ok. 278: 279: 280: evil_timers(doc) -> []; 281: evil_timers(suite) -> []; 282: evil_timers(Config) when is_list(Config) -> 283: %% Create a composite term consisting of at least: 284: %% * externals (remote pids, ports, and refs) 285: %% * large (off heap) binaries 286: %% * small (heap) binaries 287: %% * funs 288: %% * bignums 289: %% * tuples 290: %% * lists 291: %% since data of these types have to be adjusted if moved 292: %% in memory 293: ?line Self = self(), 294: ?line R1 = make_ref(), 295: ?line Node = start_slave(), 296: ?line spawn_link(Node, 297: fun () -> 298: Self ! {R1, 299: [lists:sublist(erlang:ports(), 3), 300: [make_ref(), make_ref(), make_ref()], 301: lists:sublist(processes(), 3), 302: [fun () -> gurka end, 303: fun (A) -> A + 1 end, 304: fun (A, B) -> A + B end]]} 305: end), 306: ?line ExtList = receive {R1, L} -> L end, 307: ?line stop_slave(Node), 308: ?line BinList = [<<"bla">>, 309: <<"blipp">>, 310: <<"blupp">>, 311: list_to_binary(lists:duplicate(1000000,$a)), 312: list_to_binary(lists:duplicate(1000000,$b)), 313: list_to_binary(lists:duplicate(1000000,$c))], 314: ?line FunList = [fun () -> gurka end, 315: fun (A) -> A + 1 end, 316: fun (A, B) -> A + B end], 317: ?line PidList = lists:sublist(processes(), 3), 318: ?line PortList = lists:sublist(erlang:ports(), 3), 319: ?line RefList = [make_ref(), make_ref(), make_ref()], 320: ?line BigList = [111111111111, 22222222222222, 333333333333333333], 321: ?line Msg = {BinList,[FunList,{RefList,ExtList,PidList,PortList,BigList}]}, 322: %% ?line ?t:format("Msg=~p~n",[Msg]), 323: 324: ?line Prio = process_flag(priority, max), 325: %% 326: %% In the smp case there are four major cases we want to test: 327: %% 328: %% 1. A timer started with erlang:start_timer(Time, Receiver, Msg), 329: %% where Msg is a composite term, expires, and the receivers main 330: %% lock *can not* be acquired immediately (typically when the 331: %% receiver *is* running). 332: %% 333: %% The wrap tuple ({timeout, TRef, Msg}) will in this case 334: %% be allocated in the previously allocated message buffer along 335: %% with Msg, i.e. the previously allocated message buffer will be 336: %% reallocated and potentially moved. 337: ?line TimeOutMsgs0 = evil_setup_timers(200, Self, Msg), 338: ?line RecvTimeOutMsgs0 = evil_recv_timeouts(200), 339: %% 2. A timer started with erlang:start_timer(Time, Receiver, Msg), 340: %% where Msg is an immediate term, expires, and the receivers main 341: %% lock *can not* be acquired immediately (typically when the 342: %% receiver *is* running). 343: %% 344: %% The wrap tuple will in this case be allocated in a new 345: %% message buffer. 346: ?line TimeOutMsgs1 = evil_setup_timers(200, Self, immediate), 347: ?line RecvTimeOutMsgs1 = evil_recv_timeouts(200), 348: %% 3. A timer started with erlang:start_timer(Time, Receiver, Msg), 349: %% where Msg is a composite term, expires, and the receivers main 350: %% lock *can* be acquired immediately (typically when the receiver 351: %% *is not* running). 352: %% 353: %% The wrap tuple will in this case be allocated on the receivers 354: %% heap, and Msg is passed in the previously allocated message 355: %% buffer. 356: ?line R2 = make_ref(), 357: ?line spawn_link(fun () -> 358: Self ! {R2, evil_setup_timers(200, Self, Msg)} 359: end), 360: ?line receive after 1000 -> ok end, 361: ?line TimeOutMsgs2 = receive {R2, TOM2} -> TOM2 end, 362: ?line RecvTimeOutMsgs2 = evil_recv_timeouts(200), 363: %% 4. A timer started with erlang:start_timer(Time, Receiver, Msg), 364: %% where Msg is an immediate term, expires, and the Receivers main 365: %% lock *can* be acquired immediately (typically when the receiver 366: %% *is not* running). 367: %% 368: %% The wrap tuple will in this case be allocated on the receivers 369: %% heap. 370: ?line R3 = make_ref(), 371: ?line spawn_link(fun () -> 372: Self ! {R3, evil_setup_timers(200,Self,immediate)} 373: end), 374: ?line receive after 1000 -> ok end, 375: ?line TimeOutMsgs3 = receive {R3, TOM3} -> TOM3 end, 376: ?line RecvTimeOutMsgs3 = evil_recv_timeouts(200), 377: 378: %% Garge collection will hopefully crash the emulator if something 379: %% is wrong... 380: ?line garbage_collect(), 381: ?line garbage_collect(), 382: ?line garbage_collect(), 383: 384: %% Make sure we got the timeouts we expected 385: %% 386: %% Note timeouts are *not* guaranteed to be delivered in order 387: ?line ok = match(lists:sort(RecvTimeOutMsgs0), lists:sort(TimeOutMsgs0)), 388: ?line ok = match(lists:sort(RecvTimeOutMsgs1), lists:sort(TimeOutMsgs1)), 389: ?line ok = match(lists:sort(RecvTimeOutMsgs2), lists:sort(TimeOutMsgs2)), 390: ?line ok = match(lists:sort(RecvTimeOutMsgs3), lists:sort(TimeOutMsgs3)), 391: 392: ?line process_flag(priority, Prio), 393: ?line ok. 394: 395: evil_setup_timers(N, Receiver, Msg) -> 396: ?line evil_setup_timers(0, N, Receiver, Msg, []). 397: 398: evil_setup_timers(N, N, _Receiver, _Msg, TOs) -> 399: ?line TOs; 400: evil_setup_timers(N, Max, Receiver, Msg, TOs) -> 401: ?line TRef = erlang:start_timer(N, Receiver, Msg), 402: ?line evil_setup_timers(N+1, Max, Receiver, Msg, [{timeout,TRef,Msg}|TOs]). 403: 404: 405: evil_recv_timeouts(M) -> 406: ?line evil_recv_timeouts([], 0, M). 407: 408: evil_recv_timeouts(TOs, N, N) -> 409: ?line TOs; 410: evil_recv_timeouts(TOs, N, M) -> 411: ?line receive 412: {timeout, _, _} = TO -> 413: ?line evil_recv_timeouts([TO|TOs], N+1, M) 414: after 0 -> 415: ?line evil_recv_timeouts(TOs, N, M) 416: end. 417: 418: registered_process(doc) -> []; 419: registered_process(suite) -> []; 420: registered_process(Config) when is_list(Config) -> 421: ?line Mem = mem(), 422: %% Cancel 423: ?line T1 = erlang:start_timer(500, ?MODULE, "hej"), 424: ?line T2 = erlang:send_after(500, ?MODULE, "hej"), 425: ?line undefined = whereis(?MODULE), 426: ?line true = Mem < mem(), 427: ?line true = is_integer(erlang:cancel_timer(T1)), 428: ?line true = is_integer(erlang:cancel_timer(T2)), 429: ?line false = erlang:read_timer(T1), 430: ?line false = erlang:read_timer(T2), 431: ?line Mem = mem(), 432: %% Timeout register after start 433: ?line Ref1 = make_ref(), 434: ?line T3 = erlang:start_timer(500, ?MODULE, Ref1), 435: ?line T4 = erlang:send_after(500, ?MODULE, Ref1), 436: ?line undefined = whereis(?MODULE), 437: ?line true = Mem < mem(), 438: ?line true = is_integer(erlang:read_timer(T3)), 439: ?line true = is_integer(erlang:read_timer(T4)), 440: ?line true = register(?MODULE, self()), 441: ?line receive {timeout, T3, Ref1} -> ok end, 442: ?line receive Ref1 -> ok end, 443: ?line Mem = mem(), 444: %% Timeout register before start 445: ?line Ref2 = make_ref(), 446: ?line T5 = erlang:start_timer(500, ?MODULE, Ref2), 447: ?line T6 = erlang:send_after(500, ?MODULE, Ref2), 448: ?line true = Mem < mem(), 449: ?line true = is_integer(erlang:read_timer(T5)), 450: ?line true = is_integer(erlang:read_timer(T6)), 451: ?line receive {timeout, T5, Ref2} -> ok end, 452: ?line receive Ref2 -> ok end, 453: ?line Mem = mem(), 454: ?line true = unregister(?MODULE), 455: ?line ok. 456: 457: mem() -> 458: AA = erlang:system_info(allocated_areas), 459: {value,{bif_timer,Mem}} = lists:keysearch(bif_timer, 1, AA), 460: Mem. 461: 462: process_is_cleaned_up(P) when is_pid(P) -> 463: undefined == erts_debug:get_internal_state({process_status, P}). 464: 465: wait_until(Pred) when is_function(Pred) -> 466: case catch Pred() of 467: true -> ok; 468: _ -> receive after 50 -> ok end, wait_until(Pred) 469: end. 470: 471: get(Time, Msg) -> 472: receive 473: Msg -> 474: ok 475: after Time 476: -> 477: no_message 478: end. 479: 480: get_msg() -> 481: receive 482: Msg -> 483: {ok, Msg} 484: after 0 -> 485: empty 486: end. 487: 488: start_slave() -> 489: ?line {A, B, C} = now(), 490: ?line Pa = filename:dirname(code:which(?MODULE)), 491: ?line Name = atom_to_list(?MODULE) ++ "-" ++ integer_to_list(A+B+C), 492: {ok, Node} = ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]), 493: Node. 494: 495: stop_slave(Node) -> 496: test_server:stop_node(Node). 497: 498: collect(Last) -> 499: collect(Last, []). 500: 501: receive_one() -> 502: receive 503: Msg -> 504: Msg 505: end. 506: 507: collect(Last, Msgs0) -> 508: Msg = receive_one(), 509: Msgs = Msgs0 ++ [Msg], 510: case Msg of 511: Last -> 512: Msgs; 513: _ -> 514: collect(Last, Msgs) 515: end. 516: 517: match(X, X) -> 518: %erlang:display({match, X}), 519: ok; 520: match(X, Y) -> 521: %erlang:display({mismatch, X, Y}), 522: match_aux(X, Y). 523: 524: match_aux(X, X) -> 525: unexpected_error; 526: match_aux(X, Y) when is_list(X), is_list(Y), length(X) =/= length(Y) -> 527: %% erlang:display({mismatch, X, Y}), 528: {list_length_mismatch, length(X), length(Y)}; 529: match_aux([X|Xs], [X|Ys]) -> 530: match_aux(Xs, Ys); 531: match_aux([X|_], [Y|_]) -> 532: match_aux(X, Y); 533: match_aux(X, Y) when is_tuple(X), is_tuple(Y), size(X) =/= size(Y) -> 534: %% erlang:display({mismatch, X, Y}), 535: {tuple_size_mismatch, size(X), size(Y)}; 536: match_aux(X, Y) when is_tuple(X), is_tuple(Y) -> 537: match_aux(tuple_to_list(X), tuple_to_list(Y)); 538: match_aux(X, Y) -> 539: %% erlang:display({mismatch, X, Y}), 540: {mismatch, type(X), type(Y)}. 541: 542: type(X) when is_list(X) -> list; 543: type(X) when is_tuple(X) -> tuple; 544: type(X) when is_float(X) -> float; 545: type(X) when is_integer(X) -> integer; 546: type(X) when is_pid(X) -> {pid, node(X)}; 547: type(X) when is_reference(X) -> {reference, node(X)}; 548: type(X) when is_port(X) -> {port, node(X)}; 549: type(X) when is_binary(X) -> binary; 550: type(X) when is_atom(X) -> atom; 551: type(_) -> unknown. 552: 553: