1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2018. 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-module(counters_SUITE). 21 22-include_lib("common_test/include/ct.hrl"). 23 24-export([suite/0, all/0]). 25-export([basic/1, bad/1, limits/1, indep/1, write_concurrency/1]). 26 27suite() -> [{ct_hooks,[ts_install_cth]}]. 28 29all() -> 30 [basic, bad, limits, indep, write_concurrency]. 31 32basic(Config) when is_list(Config) -> 33 Size = 10, 34 [begin 35 Ref = counters:new(Size,[Type]), 36 #{size:=Size, memory:=Memory} = counters:info(Ref), 37 check_memory(Type, Memory, Size), 38 [basic_do(Ref, Ix) || Ix <- lists:seq(1, Size)] 39 end 40 || Type <- [atomics, write_concurrency]], 41 ok. 42 43basic_do(Ref, Ix) -> 44 0 = counters:get(Ref, Ix), 45 ok = counters:add(Ref, Ix, 3), 46 3 = counters:get(Ref, Ix), 47 ok = counters:add(Ref, Ix, 14), 48 17 = counters:get(Ref, Ix), 49 ok = counters:add(Ref, Ix, -20), 50 -3 = counters:get(Ref, Ix), 51 ok = counters:add(Ref, Ix, 100), 52 97 = counters:get(Ref, Ix), 53 ok = counters:sub(Ref, Ix, 20), 54 77 = counters:get(Ref, Ix), 55 ok = counters:sub(Ref, Ix, -10), 56 87 = counters:get(Ref, Ix), 57 ok = counters:put(Ref, Ix, 0), 58 0 = counters:get(Ref, Ix), 59 ok = counters:put(Ref, Ix, 123), 60 123 = counters:get(Ref, Ix), 61 ok = counters:put(Ref, Ix, -321), 62 -321 = counters:get(Ref, Ix), 63 ok. 64 65check_memory(atomics, Memory, Size) -> 66 {_,true} = {Memory, Memory > Size*8}, 67 {_,true} = {Memory, Memory < Size*max_atomic_sz() + 100}; 68check_memory(write_concurrency, Memory, Size) -> 69 NWords = erlang:system_info(schedulers) + 1, 70 {_,true} = {Memory, Memory > NWords*Size*8}, 71 {_,true} = {Memory, Memory < NWords*(Size+7)*max_atomic_sz() + 100}. 72 73max_atomic_sz() -> 74 case erlang:system_info({wordsize, external}) of 75 4 -> 16; 76 8 -> 77 EI = erlang:system_info(ethread_info), 78 case lists:keyfind("64-bit native atomics", 1, EI) of 79 {_, "no", _} -> 16; 80 _ -> 8 81 end 82 end. 83 84bad(Config) when is_list(Config) -> 85 {'EXIT',{badarg,_}} = (catch counters:new(0,[])), 86 {'EXIT',{badarg,_}} = (catch counters:new(10,[bad])), 87 {'EXIT',{badarg,_}} = (catch counters:new(10,[atomic, bad])), 88 {'EXIT',{badarg,_}} = (catch counters:new(10,[write_concurrency | bad])), 89 Ref = counters:new(10,[]), 90 {'EXIT',{badarg,_}} = (catch counters:get(1742, 7)), 91 {'EXIT',{badarg,_}} = (catch counters:get(make_ref(), 7)), 92 {'EXIT',{badarg,_}} = (catch counters:get(Ref, -1)), 93 {'EXIT',{badarg,_}} = (catch counters:get(Ref, 0)), 94 {'EXIT',{badarg,_}} = (catch counters:get(Ref, 11)), 95 {'EXIT',{badarg,_}} = (catch counters:get(Ref, 7.0)), 96 ok. 97 98 99limits(Config) when is_list(Config) -> 100 limits_do(counters:new(1,[atomics])), 101 limits_do(counters:new(1,[write_concurrency])), 102 ok. 103 104limits_do(Ref) -> 105 Bits = 64, 106 Max = (1 bsl (Bits-1)) - 1, 107 Min = -(1 bsl (Bits-1)), 108 109 0 = counters:get(Ref, 1), 110 ok = counters:put(Ref, 1, Max), 111 Max = counters:get(Ref, 1), 112 ok = counters:add(Ref, 1, 1), 113 Min = counters:get(Ref, 1), 114 ok = counters:sub(Ref, 1, 1), 115 Max = counters:get(Ref, 1), 116 ok = counters:put(Ref, 1, Min), 117 Min = counters:get(Ref, 1), 118 119 IncrMax = (Max bsl 1) bor 1, 120 ok = counters:put(Ref, 1, 0), 121 ok = counters:add(Ref, 1, IncrMax), 122 -1 = counters:get(Ref, 1), 123 {'EXIT',{badarg,_}} = (catch counters:add(Ref, 1, IncrMax+1)), 124 {'EXIT',{badarg,_}} = (catch counters:add(Ref, 1, Min-1)), 125 {'EXIT',{badarg,_}} = (catch counters:put(Ref, 1, Max+1)), 126 {'EXIT',{badarg,_}} = (catch counters:add(Ref, 1, Min-1)), 127 ok. 128 129 130%% Verify that independent workers, using different counters 131%% within the same array, do not interfere with each other. 132indep(Config) when is_list(Config) -> 133 NScheds = erlang:system_info(schedulers), 134 Ref = counters:new(NScheds,[write_concurrency]), 135 Rounds = 100, 136 Papa = self(), 137 Pids = [spawn_opt(fun () -> 138 Val = I*197, 139 counters:put(Ref, I, Val), 140 indep_looper(Rounds, Ref, I, Val), 141 Papa ! {self(), done} 142 end, 143 [link, {scheduler, I}]) 144 || I <- lists:seq(1, NScheds)], 145 [receive {P,done} -> ok end || P <- Pids], 146 ok. 147 148indep_looper(0, _, _ , _) -> 149 ok; 150indep_looper(N, Ref, I, Val0) -> 151 %%io:format("Val0 = ~p\n", [Val0]), 152 Val0 = counters:get(Ref, I), 153 Val1 = indep_adder(Ref, I, Val0), 154 indep_subber(Ref, I, Val1), 155 Val2 = N*7 + I, 156 counters:put(Ref, I, Val2), 157 indep_looper(N-1, Ref, I, Val2). 158 159indep_adder(Ref, I, Val) when Val < (1 bsl 62) -> 160 %%io:format("adder Val = ~p\n", [Val]), 161 Incr = abs(Val div 2) + I + 984735, 162 counters:add(Ref, I, Incr), 163 Res = Val + Incr, 164 Res = counters:get(Ref, I), 165 indep_adder(Ref, I, Res); 166indep_adder(_Ref, _I, Val) -> 167 Val. 168 169indep_subber(Ref, I, Val) when Val > -(1 bsl 62) -> 170 %%io:format("subber Val = ~p\n", [Val]), 171 Decr = (abs(Val div 2) + I + 725634), 172 counters:sub(Ref, I, Decr), 173 Res = Val - Decr, 174 Res = counters:get(Ref, I), 175 indep_subber(Ref, I, Res); 176indep_subber(_Ref, _I, Val) -> 177 Val. 178 179 180 181%% Verify write_concurrency yields correct results. 182write_concurrency(Config) when is_list(Config) -> 183 rand:seed(exs1024s), 184 io:format("*** SEED: ~p ***\n", [rand:export_seed()]), 185 NScheds = erlang:system_info(schedulers), 186 Size = 100, 187 Ref = counters:new(Size,[write_concurrency]), 188 Rounds = 1000, 189 Papa = self(), 190 Pids = [spawn_opt(fun Worker() -> 191 receive 192 {go, Ix, Incr} -> 193 wc_looper(Rounds, Ref, Ix, Incr), 194 Papa ! {self(), done, Rounds*Incr}, 195 Worker(); 196 stop -> 197 ok 198 end 199 end, 200 [link, {scheduler, N}]) 201 || N <- lists:seq(1, NScheds)], 202 [begin 203 Base = rand_log64(), 204 counters:put(Ref, Index, Base), 205 SendList = [{P,{go, Index, rand_log64()}} || P <- Pids], 206 [P ! Msg || {P,Msg} <- SendList], 207 Added = lists:sum([receive {P,done,Contrib} -> Contrib end || P <- Pids]), 208 Result = mask_sint64(Base+Added), 209 {_,Result} = {Result, counters:get(Ref, Index)} 210 end 211 || Index <- lists:seq(1, Size)], 212 213 [begin unlink(P), P ! stop end || P <- Pids], 214 ok. 215 216wc_looper(0, _, _, _) -> 217 ok; 218wc_looper(N, Ref, Ix, Incr) -> 219 counters:add(Ref, Ix, Incr), 220 wc_looper(N-1, Ref, Ix, Incr). 221 222mask_sint64(X) -> 223 SMask = 1 bsl 63, 224 UMask = SMask - 1, 225 (X band UMask) - (X band SMask). 226 227%% A random signed 64-bit integer 228%% with a uniformly distributed number of significant bits. 229rand_log64() -> 230 Uint = round(math:pow(2, rand:uniform()*63)), 231 case rand:uniform(2) of 232 1 -> -Uint; 233 2 -> Uint 234 end. 235