1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 2010-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: %% 21: %% Stress tests of rwmutex implementation. 22: %% 23: %% Author: Rickard Green 24: %% 25: -module(mtx_SUITE). 26: 27: %%-define(line_trace,true). 28: 29: -include_lib("common_test/include/ct.hrl"). 30: 31: -export([all/0,suite/0,groups/0, 32: init_per_group/2,end_per_group/2, init_per_suite/1, 33: end_per_suite/1, init_per_testcase/2, end_per_testcase/2]). 34: 35: -export([long_rwlock/1, 36: hammer_ets_rwlock/1, 37: hammer_rwlock/1, 38: hammer_rwlock_check/1, 39: hammer_tryrwlock/1, 40: hammer_tryrwlock_check/1, 41: hammer_sched_long_rwlock/1, 42: hammer_sched_long_rwlock_check/1, 43: hammer_sched_long_freqread_rwlock/1, 44: hammer_sched_long_freqread_rwlock_check/1, 45: hammer_sched_long_tryrwlock/1, 46: hammer_sched_long_tryrwlock_check/1, 47: hammer_sched_long_freqread_tryrwlock/1, 48: hammer_sched_long_freqread_tryrwlock_check/1, 49: hammer_sched_rwlock/1, 50: hammer_sched_rwlock_check/1, 51: hammer_sched_freqread_rwlock/1, 52: hammer_sched_freqread_rwlock_check/1, 53: hammer_sched_tryrwlock/1, 54: hammer_sched_tryrwlock_check/1, 55: hammer_sched_freqread_tryrwlock/1, 56: hammer_sched_freqread_tryrwlock_check/1]). 57: 58: init_per_suite(Config) when is_list(Config) -> 59: DataDir = ?config(data_dir, Config), 60: Lib = filename:join([DataDir, atom_to_list(?MODULE)]), 61: case {erlang:load_nif(Lib, none),erlang:system_info(threads)} of 62: {{error,_},false} -> 63: {skip, "No thread support"}; 64: _ -> 65: Config 66: end. 67: 68: end_per_suite(Config) when is_list(Config) -> 69: catch erts_debug:set_internal_state(available_internal_state, false), 70: Config. 71: 72: init_per_testcase(_Case, Config) -> 73: Dog = ?t:timetrap(?t:minutes(15)), 74: %% Wait for deallocations to complete since we measure 75: %% runtime in test cases. 76: wait_deallocations(), 77: [{watchdog, Dog}|Config]. 78: 79: end_per_testcase(_Func, Config) -> 80: Dog = ?config(watchdog, Config), 81: ?t:timetrap_cancel(Dog). 82: 83: wait_deallocations() -> 84: try 85: erts_debug:set_internal_state(wait, deallocations) 86: catch 87: error:undef -> 88: erts_debug:set_internal_state(available_internal_state, true), 89: wait_deallocations() 90: end. 91: 92: suite() -> [{ct_hooks,[ts_install_cth]}]. 93: 94: all() -> 95: [long_rwlock, hammer_rwlock_check, hammer_rwlock, 96: hammer_tryrwlock_check, hammer_tryrwlock, 97: hammer_ets_rwlock, hammer_sched_long_rwlock_check, 98: hammer_sched_long_rwlock, 99: hammer_sched_long_freqread_rwlock_check, 100: hammer_sched_long_freqread_rwlock, 101: hammer_sched_long_tryrwlock_check, 102: hammer_sched_long_tryrwlock, 103: hammer_sched_long_freqread_tryrwlock_check, 104: hammer_sched_long_freqread_tryrwlock, 105: hammer_sched_rwlock_check, hammer_sched_rwlock, 106: hammer_sched_freqread_rwlock_check, 107: hammer_sched_freqread_rwlock, 108: hammer_sched_tryrwlock_check, hammer_sched_tryrwlock, 109: hammer_sched_freqread_tryrwlock_check, 110: hammer_sched_freqread_tryrwlock]. 111: 112: groups() -> 113: []. 114: 115: init_per_group(_GroupName, Config) -> 116: Config. 117: 118: end_per_group(_GroupName, Config) -> 119: Config. 120: 121: 122: long_rwlock(Config) when is_list(Config) -> 123: statistics(runtime), 124: LLRes = long_rw_test(), 125: {_, RunTime} = statistics(runtime), 126: %% A very short run time is expected, since 127: %% threads in the test mostly wait 128: ?t:format("RunTime=~p~n", [RunTime]), 129: ?line true = RunTime < 400, 130: ?line RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.", 131: case LLRes of 132: ok -> 133: {comment, RunTimeStr}; 134: {comment, Comment} -> 135: {comment, Comment ++ " " ++ RunTimeStr} 136: end. 137: 138: hammer_rwlock(Config) when is_list(Config) -> 139: hammer_rw_test(false). 140: 141: hammer_rwlock_check(Config) when is_list(Config) -> 142: hammer_rw_test(true). 143: 144: hammer_tryrwlock(Config) when is_list(Config) -> 145: hammer_tryrw_test(false). 146: 147: hammer_tryrwlock_check(Config) when is_list(Config) -> 148: hammer_tryrw_test(true). 149: 150: hammer_sched_rwlock(Config) when is_list(Config) -> 151: hammer_sched_rwlock_test(false, false, true, 0, 0). 152: 153: hammer_sched_rwlock_check(Config) when is_list(Config) -> 154: hammer_sched_rwlock_test(false, true, true, 0, 0). 155: 156: hammer_sched_freqread_rwlock(Config) when is_list(Config) -> 157: hammer_sched_rwlock_test(true, false, true, 0, 0). 158: 159: hammer_sched_freqread_rwlock_check(Config) when is_list(Config) -> 160: hammer_sched_rwlock_test(true, true, true, 0, 0). 161: 162: hammer_sched_tryrwlock(Config) when is_list(Config) -> 163: hammer_sched_rwlock_test(false, false, false, 0, 100). 164: 165: hammer_sched_tryrwlock_check(Config) when is_list(Config) -> 166: hammer_sched_rwlock_test(false, true, false, 0, 100). 167: 168: hammer_sched_freqread_tryrwlock(Config) when is_list(Config) -> 169: hammer_sched_rwlock_test(true, false, false, 0, 100). 170: 171: hammer_sched_freqread_tryrwlock_check(Config) when is_list(Config) -> 172: hammer_sched_rwlock_test(true, true, false, 0, 100). 173: 174: hammer_sched_long_rwlock(Config) when is_list(Config) -> 175: hammer_sched_rwlock_test(false, false, true, 100, 0). 176: 177: hammer_sched_long_rwlock_check(Config) when is_list(Config) -> 178: hammer_sched_rwlock_test(false, true, true, 100, 0). 179: 180: hammer_sched_long_freqread_rwlock(Config) when is_list(Config) -> 181: hammer_sched_rwlock_test(true, false, true, 100, 0). 182: 183: hammer_sched_long_freqread_rwlock_check(Config) when is_list(Config) -> 184: hammer_sched_rwlock_test(true, true, true, 100, 0). 185: 186: hammer_sched_long_tryrwlock(Config) when is_list(Config) -> 187: hammer_sched_rwlock_test(false, false, false, 100, 100). 188: 189: hammer_sched_long_tryrwlock_check(Config) when is_list(Config) -> 190: hammer_sched_rwlock_test(false, true, false, 100, 100). 191: 192: hammer_sched_long_freqread_tryrwlock(Config) when is_list(Config) -> 193: hammer_sched_rwlock_test(true, false, false, 100, 100). 194: 195: hammer_sched_long_freqread_tryrwlock_check(Config) when is_list(Config) -> 196: hammer_sched_rwlock_test(true, true, false, 100, 100). 197: 198: hammer_sched_rwlock_test(FreqRead, LockCheck, Blocking, WaitLocked, WaitUnlocked) -> 199: case create_rwlock(FreqRead, LockCheck) of 200: enotsup -> 201: {skipped, "Not supported."}; 202: RWLock -> 203: Onln = erlang:system_info(schedulers_online), 204: NWPs = case Onln div 3 of 205: 1 -> case Onln < 4 of 206: true -> 1; 207: false -> 2 208: end; 209: X -> X 210: end, 211: NRPs = Onln - NWPs, 212: NoLockOps = ((((50000000 div Onln) 213: div case {Blocking, WaitLocked} of 214: {false, 0} -> 1; 215: _ -> 10 216: end) 217: div (case WaitLocked == 0 of 218: true -> 1; 219: false -> WaitLocked*250 220: end)) 221: div handicap()), 222: ?t:format("NoLockOps=~p~n", [NoLockOps]), 223: Sleep = case Blocking of 224: true -> NoLockOps; 225: false -> NoLockOps div 10 226: end, 227: WPs = lists:map( 228: fun (Sched) -> 229: spawn_opt( 230: fun () -> 231: io:format("Writer on scheduler ~p.~n", 232: [Sched]), 233: Sched = erlang:system_info(scheduler_id), 234: receive go -> gone end, 235: hammer_sched_rwlock_proc(RWLock, 236: Blocking, 237: true, 238: WaitLocked, 239: WaitUnlocked, 240: NoLockOps, 241: Sleep), 242: Sched = erlang:system_info(scheduler_id) 243: end, 244: [link, {scheduler, Sched}]) 245: end, 246: lists:seq(1, NWPs)), 247: RPs = lists:map( 248: fun (Sched) -> 249: spawn_opt( 250: fun () -> 251: io:format("Reader on scheduler ~p.~n", 252: [Sched]), 253: Sched = erlang:system_info(scheduler_id), 254: receive go -> gone end, 255: hammer_sched_rwlock_proc(RWLock, 256: Blocking, 257: false, 258: WaitLocked, 259: WaitUnlocked, 260: NoLockOps, 261: Sleep), 262: Sched = erlang:system_info(scheduler_id) 263: end, 264: [link, {scheduler, Sched}]) 265: end, 266: lists:seq(NWPs + 1, NWPs + NRPs)), 267: Procs = WPs ++ RPs, 268: case {Blocking, WaitLocked} of 269: {_, 0} -> ok; 270: {false, _} -> ok; 271: _ -> statistics(runtime) 272: end, 273: lists:foreach(fun (P) -> P ! go end, Procs), 274: lists:foreach(fun (P) -> 275: M = erlang:monitor(process, P), 276: receive 277: {'DOWN', M, process, P, _} -> 278: ok 279: end 280: end, 281: Procs), 282: case {Blocking, WaitLocked} of 283: {_, 0} -> ok; 284: {false, _} -> ok; 285: _ -> 286: {_, RunTime} = statistics(runtime), 287: ?t:format("RunTime=~p~n", [RunTime]), 288: ?line true = RunTime < 700, 289: {comment, 290: "Run-time during test was " 291: ++ integer_to_list(RunTime) 292: ++ " ms."} 293: end 294: end. 295: 296: hammer_sched_rwlock_proc(_RWLock, 297: _Blocking, 298: _WriteOp, 299: _WaitLocked, 300: _WaitUnlocked, 301: 0, 302: _Sleep) -> 303: ok; 304: hammer_sched_rwlock_proc(RWLock, 305: Blocking, 306: WriteOp, 307: WaitLocked, 308: WaitUnlocked, 309: Times, 310: Sleep) when Times rem Sleep == 0 -> 311: rwlock_op(RWLock, Blocking, WriteOp, WaitLocked, WaitUnlocked), 312: hammer_sched_rwlock_proc(RWLock, 313: Blocking, 314: WriteOp, 315: WaitLocked, 316: WaitUnlocked, 317: Times - 1, 318: Sleep); 319: hammer_sched_rwlock_proc(RWLock, 320: Blocking, 321: WriteOp, 322: WaitLocked, 323: WaitUnlocked, 324: Times, 325: Sleep) -> 326: rwlock_op(RWLock, Blocking, WriteOp, WaitLocked, 0), 327: hammer_sched_rwlock_proc(RWLock, 328: Blocking, 329: WriteOp, 330: WaitLocked, 331: WaitUnlocked, 332: Times - 1, 333: Sleep). 334: 335: -define(HAMMER_ETS_RWLOCK_REPEAT_TIMES, 1). 336: -define(HAMMER_ETS_RWLOCK_TSIZE, 500). 337: 338: hammer_ets_rwlock(Config) when is_list(Config) -> 339: {Ops, Procs} = case handicap() of 340: 1 -> {20000, 500}; 341: 2 -> {20000, 50}; 342: 3 -> {2000, 50}; 343: _ -> {200, 50} 344: end, 345: ?t:format("Procs=~p~nOps=~p~n", [Procs, Ops]), 346: lists:foreach(fun (XOpts) -> 347: ?t:format("Running with extra opts: ~p", [XOpts]), 348: hammer_ets_rwlock_test(XOpts, true, 2, Ops, 349: Procs, false) 350: end, 351: [[], 352: [{read_concurrency, true}], 353: [{write_concurrency, true}], 354: [{read_concurrency, true},{write_concurrency, true}]]), 355: ok. 356: 357: %% Aux funcs 358: 359: long_rw_test() -> 360: exit(no_nif_implementation). 361: 362: hammer_rw_test(_Arg) -> 363: exit(no_nif_implementation). 364: 365: hammer_tryrw_test(_Arg) -> 366: exit(no_nif_implementation). 367: 368: create_rwlock(_FreqRead, _LockCheck) -> 369: exit(no_nif_implementation). 370: 371: rwlock_op(_RWLock, _Blocking, _WriteOp, _WaitLocked, _WaitUnlocked) -> 372: exit(no_nif_implementation). 373: 374: hammer_ets_rwlock_put_data() -> 375: put(?MODULE, {"here are some", data, "to store", make_ref()}). 376: 377: hammer_ets_rwlock_get_data() -> 378: get(?MODULE). 379: 380: hammer_ets_rwlock_ops(_T, _UW, _N, _C, _SC, 0) -> 381: ok; 382: hammer_ets_rwlock_ops(T, UW, N, C, SC, Tot) when N >= ?HAMMER_ETS_RWLOCK_TSIZE -> 383: hammer_ets_rwlock_ops(T, UW, 0, C, SC, Tot); 384: hammer_ets_rwlock_ops(T, UW, N, 0, SC, Tot) -> 385: case UW of 386: true -> 387: true = ets:insert(T, {N, Tot, hammer_ets_rwlock_get_data()}); 388: false -> 389: [{N, _, _}] = ets:lookup(T, N) 390: end, 391: hammer_ets_rwlock_ops(T, UW, N+1, SC, SC, Tot-1); 392: hammer_ets_rwlock_ops(T, UW, N, C, SC, Tot) -> 393: case UW of 394: false -> 395: true = ets:insert(T, {N, Tot, hammer_ets_rwlock_get_data()}); 396: true -> 397: [{N, _, _}] = ets:lookup(T, N) 398: end, 399: hammer_ets_rwlock_ops(T, UW, N+1, C-1, SC, Tot-1). 400: 401: hammer_ets_rwlock_init(T, N) when N < ?HAMMER_ETS_RWLOCK_TSIZE -> 402: ets:insert(T, {N, N, N}), 403: hammer_ets_rwlock_init(T, N+1); 404: hammer_ets_rwlock_init(_T, _N) -> 405: ok. 406: 407: hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) -> 408: receive after 100 -> ok end, 409: {TP, TM} = spawn_monitor( 410: fun () -> 411: _L = repeat_list( 412: fun () -> 413: Caller = self(), 414: T = fun () -> 415: Parent = self(), 416: hammer_ets_rwlock_put_data(), 417: T=ets:new(x, [public | XOpts]), 418: hammer_ets_rwlock_init(T, 0), 419: Ps0 = repeat_list( 420: fun () -> 421: spawn_link( 422: fun () -> 423: hammer_ets_rwlock_put_data(), 424: receive go -> ok end, 425: hammer_ets_rwlock_ops(T, UW, N, C, C, N), 426: Parent ! {done, self()}, 427: receive after infinity -> ok end 428: end) 429: end, 430: NP - case SC of 431: false -> 0; 432: _ -> 1 433: end), 434: Ps = case SC of 435: false -> Ps0; 436: _ -> [spawn_link(fun () -> 437: hammer_ets_rwlock_put_data(), 438: receive go -> ok end, 439: hammer_ets_rwlock_ops(T, UW, N, SC, SC, N), 440: Parent ! {done, self()}, 441: receive after infinity -> ok end 442: end) | Ps0] 443: end, 444: Start = now(), 445: lists:foreach(fun (P) -> P ! go end, Ps), 446: lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps), 447: Stop = now(), 448: lists:foreach(fun (P) -> 449: unlink(P), 450: exit(P, bang), 451: M = erlang:monitor(process, P), 452: receive 453: {'DOWN', M, process, P, _} -> ok 454: end 455: end, Ps), 456: Res = timer:now_diff(Stop, Start)/1000000, 457: Caller ! {?MODULE, self(), Res} 458: end, 459: TP = spawn_link(T), 460: receive 461: {?MODULE, TP, Res} -> 462: Res 463: end 464: end, 465: ?HAMMER_ETS_RWLOCK_REPEAT_TIMES) 466: end), 467: receive 468: {'DOWN', TM, process, TP, _} -> ok 469: end. 470: 471: repeat_list(Fun, N) -> 472: repeat_list(Fun, N, []). 473: 474: repeat_list(_Fun, 0, Acc) -> 475: Acc; 476: repeat_list(Fun, N, Acc) -> 477: repeat_list(Fun, N-1, [Fun()|Acc]). 478: 479: 480: handicap() -> 481: X0 = case catch (erlang:system_info(logical_processors_available) >= 482: erlang:system_info(schedulers_online)) of 483: true -> 1; 484: _ -> 2 485: end, 486: case erlang:system_info(build_type) of 487: opt -> 488: X0; 489: ReallySlow when ReallySlow == debug; 490: ReallySlow == valgrind; 491: ReallySlow == purify -> 492: X0*3; 493: _Slow -> 494: X0*2 495: end. 496: