%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2017. 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% -module(efile_SUITE). -export([all/0, suite/0]). -export([iter_max_files/1, async_dist/1]). -export([do_iter_max_files/2, do_async_dist/1]). -include_lib("common_test/include/ct.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [iter_max_files, async_dist]. do_async_dist(Dir) -> X = 100, AT = erlang:system_info(thread_pool_size), Keys = file_keys(Dir,AT*X,[],[]), Tab = ets:new(x,[ordered_set]), [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ], [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ], Res = [ V || {_,V} <- ets:tab2list(Tab) ], ets:delete(Tab), {Res, sdev(Res)/X}. sdev(List) -> Len = length(List), Mean = lists:sum(List)/Len, math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len). file_keys(_,0,FdList,FnList) -> [ file:close(FD) || FD <- FdList ], [ file:delete(FN) || FN <- FnList ], []; file_keys(Dir,Num,FdList,FnList) -> Name = "dummy"++integer_to_list(Num), FN = filename:join([Dir,Name]), case file:open(FN,[write,raw]) of {ok,FD} -> {file_descriptor,prim_file,{Port,_}} = FD, <> = iolist_to_binary(erlang:port_control(Port,$K,[])), [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])]; {error,_} -> % Try freeing up FD's if there are any case FdList of [] -> exit({cannot_open_file,FN}); _ -> [ file:close(FD) || FD <- FdList ], [ file:delete(F) || F <- FnList ], file_keys(Dir,Num,[],[]) end end. %% Check that the distribution of files over async threads is fair async_dist(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir,Config), Dir = filename:dirname(code:which(?MODULE)), AsyncSizes = [7,10,100,255,256,64,63,65], Max = 0.5, lists:foreach(fun(Size) -> {ok,Node} = test_server:start_node (test_iter_max_files,slave, [{args, "+A "++integer_to_list(Size)++ " -pa " ++ Dir}]), {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist, [DataDir]), test_server:stop_node(Node), if SD > Max -> io:format("Bad async queue distribution for " "~p async threads:~n" " Standard deviation is ~p~n" " Key distribution:~n ~lp~n", [Size,SD,Distr]), exit({bad_async_dist,Size,SD,Distr}); true -> io:format("OK async queue distribution for " "~p async threads:~n" " Standard deviation is ~p~n" " Key distribution:~n ~lp~n", [Size,SD,Distr]), ok end end, AsyncSizes), ok. %% %% Open as many files as possible. Do this several times and check %% that we get the same number of files every time. %% iter_max_files(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir,Config), TestFile = filename:join(DataDir, "existing_file"), N = 10, %% Run on a different node in order to set the max ports Dir = filename:dirname(code:which(?MODULE)), {ok,Node} = test_server:start_node(test_iter_max_files,slave, [{args,"+Q 1524 -pa " ++ Dir}]), L = rpc:call(Node,?MODULE,do_iter_max_files,[N, TestFile]), test_server:stop_node(Node), io:format("Number of files opened in each test:~n~w\n", [L]), all_equal(L), Head = hd(L), if Head >= 2 -> ok; true -> ct:fail(too_few_files) end, {comment, "Max files: " ++ integer_to_list(hd(L))}. do_iter_max_files(N, Name) when N > 0 -> [max_files(Name)| do_iter_max_files(N-1, Name)]; do_iter_max_files(_, _) -> []. all_equal([E, E| T]) -> all_equal([E| T]); all_equal([_]) -> ok; all_equal([]) -> ok. max_files(Name) -> Fds = open_files(Name), N = length(Fds), close_files(Fds), N. close_files([Fd| Fds]) -> file:close(Fd), close_files(Fds); close_files([]) -> ok. open_files(Name) -> case file:open(Name, [read,raw]) of {ok, Fd} -> [Fd| open_files(Name)]; {error, _Reason} -> % io:format("Error reason: ~p", [_Reason]), [] end.