aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/mtx_SUITE.erl
blob: c7c043d8e74ec80661ba498f10aa68e5aebfb3a5 (plain) (tree)
1
2
3
4
5


                   
                                                        
  










                                                                           












                                          
                                           
 

                                                          























                                                      





















                                                          


                                                          





                                                                    

                                             
                                                                         


                                   


                                                          
           
 
                                  
       
 








                                                                          






                                               
                               





























































































































































                                                                                     
                                               


























































































































































                                                                                                                                 
                                                                                    

                                                                                                                  
                                                                                   







                                                                                                              
                                                                                                                  







































                                                                                   
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2013. 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%
%%

%%
%% Stress tests of rwmutex implementation.
%%
%% Author: Rickard Green
%%
-module(mtx_SUITE).

%%-define(line_trace,true).

-include_lib("common_test/include/ct.hrl").

-export([all/0,suite/0, init_per_suite/1, end_per_suite/1,
         init_per_testcase/2, end_per_testcase/2]).

-export([long_rwlock/1,
	 hammer_ets_rwlock/1,
	 hammer_rwlock/1,
	 hammer_rwlock_check/1,
	 hammer_tryrwlock/1,
	 hammer_tryrwlock_check/1,
	 hammer_sched_long_rwlock/1,
	 hammer_sched_long_rwlock_check/1,
	 hammer_sched_long_freqread_rwlock/1,
	 hammer_sched_long_freqread_rwlock_check/1,
	 hammer_sched_long_tryrwlock/1,
	 hammer_sched_long_tryrwlock_check/1,
	 hammer_sched_long_freqread_tryrwlock/1,
	 hammer_sched_long_freqread_tryrwlock_check/1,
	 hammer_sched_rwlock/1,
	 hammer_sched_rwlock_check/1,
	 hammer_sched_freqread_rwlock/1,
	 hammer_sched_freqread_rwlock_check/1,
	 hammer_sched_tryrwlock/1,
	 hammer_sched_tryrwlock_check/1,
	 hammer_sched_freqread_tryrwlock/1,
	 hammer_sched_freqread_tryrwlock_check/1]).

suite() ->
    [{ct_hooks,[ts_install_cth]},
     {timetrap, {minutes, 15}}].

all() ->
    [long_rwlock, hammer_rwlock_check, hammer_rwlock,
     hammer_tryrwlock_check, hammer_tryrwlock,
     hammer_ets_rwlock, hammer_sched_long_rwlock_check,
     hammer_sched_long_rwlock,
     hammer_sched_long_freqread_rwlock_check,
     hammer_sched_long_freqread_rwlock,
     hammer_sched_long_tryrwlock_check,
     hammer_sched_long_tryrwlock,
     hammer_sched_long_freqread_tryrwlock_check,
     hammer_sched_long_freqread_tryrwlock,
     hammer_sched_rwlock_check, hammer_sched_rwlock,
     hammer_sched_freqread_rwlock_check,
     hammer_sched_freqread_rwlock,
     hammer_sched_tryrwlock_check, hammer_sched_tryrwlock,
     hammer_sched_freqread_tryrwlock_check,
     hammer_sched_freqread_tryrwlock].

init_per_suite(Config) when is_list(Config) ->
    DataDir = ?config(data_dir, Config),
    Lib = filename:join([DataDir, atom_to_list(?MODULE)]),
    case {erlang:load_nif(Lib, none),erlang:system_info(threads)} of
	{{error,_},false} ->
	    {skip, "No thread support"};
	_ ->
	    Config
    end.

end_per_suite(Config) when is_list(Config) ->
    catch erts_debug:set_internal_state(available_internal_state, false),
    Config.

init_per_testcase(_Case, Config) ->
    %% Wait for deallocations to complete since we measure
    %% runtime in test cases.
    wait_deallocations(),
    Config.

end_per_testcase(_Func, Config) ->
    ok.

wait_deallocations() ->
    try
	erts_debug:set_internal_state(wait, deallocations)
    catch
	error:undef ->
	    erts_debug:set_internal_state(available_internal_state, true),
	    wait_deallocations()
    end.

long_rwlock(Config) when is_list(Config) ->
    statistics(runtime),
    LLRes = long_rw_test(),
    {_, RunTime} = statistics(runtime),
    %% A very short run time is expected, since
    %% threads in the test mostly wait
    ?t:format("RunTime=~p~n", [RunTime]),
    ?line true = RunTime < 400,
    ?line RunTimeStr = "Run-time during test was "++integer_to_list(RunTime)++" ms.",
    case LLRes of
	ok ->
	    {comment, RunTimeStr};
	{comment, Comment} ->
	    {comment, Comment ++ " " ++ RunTimeStr}
    end.

hammer_rwlock(Config) when is_list(Config) ->
    hammer_rw_test(false).

hammer_rwlock_check(Config) when is_list(Config) ->
    hammer_rw_test(true).

hammer_tryrwlock(Config) when is_list(Config) ->
    hammer_tryrw_test(false).

hammer_tryrwlock_check(Config) when is_list(Config) ->
    hammer_tryrw_test(true).

hammer_sched_rwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, false, true, 0, 0).

hammer_sched_rwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, true, true, 0, 0).

hammer_sched_freqread_rwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, false, true, 0, 0).

hammer_sched_freqread_rwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, true, true, 0, 0).

hammer_sched_tryrwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, false, false, 0, 100).

hammer_sched_tryrwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, true, false, 0, 100).

hammer_sched_freqread_tryrwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, false, false, 0, 100).

hammer_sched_freqread_tryrwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, true, false, 0, 100).

hammer_sched_long_rwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, false, true, 100, 0).

hammer_sched_long_rwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, true, true, 100, 0).

hammer_sched_long_freqread_rwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, false, true, 100, 0).

hammer_sched_long_freqread_rwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, true, true, 100, 0).

hammer_sched_long_tryrwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, false, false, 100, 100).

hammer_sched_long_tryrwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(false, true, false, 100, 100).

hammer_sched_long_freqread_tryrwlock(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, false, false, 100, 100).

hammer_sched_long_freqread_tryrwlock_check(Config) when is_list(Config) ->
    hammer_sched_rwlock_test(true, true, false, 100, 100).

hammer_sched_rwlock_test(FreqRead, LockCheck, Blocking, WaitLocked, WaitUnlocked) ->
    case create_rwlock(FreqRead, LockCheck) of
	enotsup ->
	    {skipped, "Not supported."};
	RWLock ->
	    Onln = erlang:system_info(schedulers_online),
	    NWPs = case Onln div 3 of
		       1 -> case Onln < 4 of
				true -> 1;
				false -> 2
			    end;
		       X -> X
		   end,
	    NRPs = Onln - NWPs,
	    NoLockOps = ((((50000000 div Onln)
			       div case {Blocking, WaitLocked} of
				       {false, 0} -> 1;
				       _ -> 10
				   end)
			      div (case WaitLocked == 0 of
				       true -> 1;
				       false -> WaitLocked*250
				   end))
			     div handicap()),
	    ?t:format("NoLockOps=~p~n", [NoLockOps]),
	    Sleep = case Blocking of
			true -> NoLockOps;
			false -> NoLockOps div 10
		    end,
	    WPs = lists:map(
		    fun (Sched) ->
			    spawn_opt(
			      fun () ->
				      io:format("Writer on scheduler ~p.~n",
						[Sched]),
				      Sched = erlang:system_info(scheduler_id),
				      receive go -> gone end,
				      hammer_sched_rwlock_proc(RWLock,
							       Blocking,
							       true,
							       WaitLocked,
							       WaitUnlocked,
							       NoLockOps,
							       Sleep),
				      Sched = erlang:system_info(scheduler_id)
			      end,
			      [link, {scheduler, Sched}])
		    end,
		    lists:seq(1, NWPs)),
	    RPs = lists:map(
		    fun (Sched) ->
			    spawn_opt(
			      fun () ->
				      io:format("Reader on scheduler ~p.~n",
						[Sched]),
				      Sched = erlang:system_info(scheduler_id),
				      receive go -> gone end,
				      hammer_sched_rwlock_proc(RWLock,
							       Blocking,
							       false,
							       WaitLocked,
							       WaitUnlocked,
							       NoLockOps,
							       Sleep),
				      Sched = erlang:system_info(scheduler_id)
			      end,
			      [link, {scheduler, Sched}])
		    end,
		    lists:seq(NWPs + 1, NWPs + NRPs)),
	    Procs = WPs ++ RPs,
	    case {Blocking, WaitLocked} of
		{_, 0} -> ok;
		{false, _} -> ok;
		_ -> statistics(runtime)
	    end,
	    lists:foreach(fun (P) -> P ! go end, Procs),
	    lists:foreach(fun (P) ->
				  M = erlang:monitor(process, P),
				  receive
				      {'DOWN', M, process, P, _} ->
					  ok
				  end
			  end,
			  Procs),
	    case {Blocking, WaitLocked} of
		{_, 0} -> ok;
		{false, _} -> ok;
		_ ->
		    {_, RunTime} = statistics(runtime),
		    ?t:format("RunTime=~p~n", [RunTime]),
		    ?line true = RunTime < 700,
		    {comment,
		     "Run-time during test was "
		     ++ integer_to_list(RunTime)
		     ++ " ms."}
	    end
    end.

hammer_sched_rwlock_proc(_RWLock,
			 _Blocking,
			 _WriteOp,
			 _WaitLocked,
			 _WaitUnlocked,
			 0,
			 _Sleep) ->
    ok;
hammer_sched_rwlock_proc(RWLock,
			 Blocking,
			 WriteOp,
			 WaitLocked,
			 WaitUnlocked,
			 Times,
			 Sleep) when Times rem Sleep == 0 ->
    rwlock_op(RWLock, Blocking, WriteOp, WaitLocked, WaitUnlocked),
    hammer_sched_rwlock_proc(RWLock,
			     Blocking,
			     WriteOp,
			     WaitLocked,
			     WaitUnlocked,
			     Times - 1,
			     Sleep);
hammer_sched_rwlock_proc(RWLock,
			 Blocking,
			 WriteOp,
			 WaitLocked,
			 WaitUnlocked,
			 Times,
			 Sleep) ->
    rwlock_op(RWLock, Blocking, WriteOp, WaitLocked, 0),
    hammer_sched_rwlock_proc(RWLock,
			     Blocking,
			     WriteOp,
			     WaitLocked,
			     WaitUnlocked,
			     Times - 1,
			     Sleep).

-define(HAMMER_ETS_RWLOCK_REPEAT_TIMES, 1).
-define(HAMMER_ETS_RWLOCK_TSIZE, 500).

hammer_ets_rwlock(Config) when is_list(Config) ->
    {Ops, Procs} = case handicap() of
		       1 -> {20000, 500};
		       2 -> {20000, 50};
		       3 -> {2000, 50};
		       _ -> {200, 50}
		   end,
    ?t:format("Procs=~p~nOps=~p~n", [Procs, Ops]),
    lists:foreach(fun (XOpts) ->
			  ?t:format("Running with extra opts: ~p", [XOpts]),
			  hammer_ets_rwlock_test(XOpts, true, 2, Ops,
						 Procs, false)
		  end,
		  [[],
		   [{read_concurrency, true}],
		   [{write_concurrency, true}],
		   [{read_concurrency, true},{write_concurrency, true}]]),
    ok.

%% Aux funcs

long_rw_test() ->
    exit(no_nif_implementation).

hammer_rw_test(_Arg) ->
    exit(no_nif_implementation).

hammer_tryrw_test(_Arg) ->
    exit(no_nif_implementation).

create_rwlock(_FreqRead, _LockCheck) ->
    exit(no_nif_implementation).

rwlock_op(_RWLock, _Blocking, _WriteOp, _WaitLocked, _WaitUnlocked) ->
    exit(no_nif_implementation).

hammer_ets_rwlock_put_data() ->
    put(?MODULE, {"here are some", data, "to store", make_ref()}).

hammer_ets_rwlock_get_data() ->
    get(?MODULE).

hammer_ets_rwlock_ops(_T, _UW, _N, _C, _SC, 0) ->
    ok;
hammer_ets_rwlock_ops(T, UW, N, C, SC, Tot) when N >= ?HAMMER_ETS_RWLOCK_TSIZE ->
    hammer_ets_rwlock_ops(T, UW, 0, C, SC, Tot);
hammer_ets_rwlock_ops(T, UW, N, 0, SC, Tot) ->
    case UW of
	true ->
	    true = ets:insert(T, {N, Tot, hammer_ets_rwlock_get_data()});
	false ->
	    [{N, _, _}] = ets:lookup(T, N)
    end,
    hammer_ets_rwlock_ops(T, UW, N+1, SC, SC, Tot-1);
hammer_ets_rwlock_ops(T, UW, N, C, SC, Tot) ->
    case UW of
	false ->
	    true = ets:insert(T, {N, Tot, hammer_ets_rwlock_get_data()});
	true ->
	    [{N, _, _}] = ets:lookup(T, N)
    end,
    hammer_ets_rwlock_ops(T, UW, N+1, C-1, SC, Tot-1).

hammer_ets_rwlock_init(T, N) when N < ?HAMMER_ETS_RWLOCK_TSIZE ->
    ets:insert(T, {N, N, N}),
    hammer_ets_rwlock_init(T, N+1);
hammer_ets_rwlock_init(_T, _N) ->
    ok.

hammer_ets_rwlock_test(XOpts, UW, C, N, NP, SC) ->
    receive after 100 -> ok end,
    {TP, TM} = spawn_monitor(
		 fun () ->
			 _L = repeat_list(
				fun () ->
					Caller = self(),
					T = fun () ->
						    Parent = self(),
						    hammer_ets_rwlock_put_data(),
						    T=ets:new(x, [public | XOpts]),
						    hammer_ets_rwlock_init(T, 0),
						    Ps0 = repeat_list(
							    fun () ->
								    spawn_link(
								      fun () ->
									      hammer_ets_rwlock_put_data(),
									      receive go -> ok end,
									      hammer_ets_rwlock_ops(T, UW, N, C, C, N),
									      Parent ! {done, self()},
									      receive after infinity -> ok end
								      end)
							    end,
							    NP - case SC of
								     false -> 0;
								     _ -> 1
								 end),
						    Ps = case SC of
							     false -> Ps0;
							     _ -> [spawn_link(fun () ->
										      hammer_ets_rwlock_put_data(),
										      receive go -> ok end,
										      hammer_ets_rwlock_ops(T, UW, N, SC, SC, N),
										      Parent ! {done, self()},
										      receive after infinity -> ok end
									      end) | Ps0]
							 end,
						    Start = erlang:monotonic_time(),
						    lists:foreach(fun (P) -> P ! go end, Ps),
						    lists:foreach(fun (P) -> receive {done, P} -> ok end end, Ps),
						    Stop = erlang:monotonic_time(),
						    lists:foreach(fun (P) ->
									  unlink(P),
									  exit(P, bang),
									  M = erlang:monitor(process, P),
									  receive
									      {'DOWN', M, process, P, _} -> ok
									  end
								  end, Ps),
						    Res = (Stop-Start)/erlang:convert_time_unit(1,seconds,native),
						    Caller ! {?MODULE, self(), Res}
					    end,
					TP = spawn_link(T),
					receive
					    {?MODULE, TP, Res} ->
						Res
					end
				end,
				?HAMMER_ETS_RWLOCK_REPEAT_TIMES)
		 end),
    receive
	{'DOWN', TM, process, TP, _} -> ok
    end.

repeat_list(Fun, N) ->
    repeat_list(Fun, N, []).

repeat_list(_Fun, 0, Acc) ->
    Acc;
repeat_list(Fun, N, Acc) ->
    repeat_list(Fun, N-1, [Fun()|Acc]).


handicap() ->
    X0 = case catch (erlang:system_info(logical_processors_available) >=
			 erlang:system_info(schedulers_online)) of
	     true -> 1;
	     _ -> 2
	 end,
    case erlang:system_info(build_type) of
	opt ->
	    X0;
	ReallySlow when ReallySlow == debug;
			ReallySlow == valgrind;
			ReallySlow == purify ->
	    X0*3;
	_Slow ->
	    X0*2
    end.