ETS 'set' table bug ... or not?
Scott Lystig Fritchie
fritchie@REDACTED
Sat Jul 12 20:25:16 CEST 2003
Hi there. While running some tests on a borrowed Pentium 4 Xeon
RedHat 8.0 Linux box, I encountered a segmentation violation problem
that puzzled me. I'm not certain if it's a bug in ETS or if it is a
problem with the hardware. The hardware claims to have ECC memory, so
it's unlikely (but not impossible, I suppose) that bad RAM is causing
the problem.
The code for test.erl is included below. If I run the command line:
erl -noinput -s test do set insert_rand 7000 delete_all \
traverse_forward -s erlang halt
... no problem. If I use a bigger number than 7000, no problem
... until I use this number: 53999154. Then I get a segmentation
violation every time, apparently while the 'delete_all' part of the
test is running.
WARNING: The process size grows to about 2.6GB.
I was borrowing CPU cycles on this machine. Alas, I no longer have
convenient access to that machine. Could someone else with a 3-4GB
Pentium 4 box check to see if they can reproduce the problem? It
would be good to know if this is an actual ETS bug. (I was using
R9B-1 for my testing.)
-Scott
--- snip --- snip --- snip --- snip --- snip --- snip --- snip ---
-module(test).
-export([do/1]).
%%% Internal exports
-export([
delete_all/0, delete_all/2,
insert_rand/0, insert_rand/2,
traverse_forward/0, traverse_forward/2
]).
%%% Debugging exports
-export([unatomify/1, test_defined/1]).
%%% We use single arg style for do() so that we can automate via
%%% "erl -s test2 do bag insert_seq 400" or
%%% "erl -s test2 do set insert 11000000 3 traverse_forw lookup_seq 11000000"
do(ArgList) when list(ArgList) ->
[TabType|AList] = unatomify(ArgList),
Tab = ets:new(t, [TabType, private]),
Res = do2(Tab, AList),
format_results(Res),
Res.
do2(Tab, AList) ->
FAndAList = parse_alist(AList),
[{DoFunc, timer:tc(?MODULE, DoFunc, [Args, Tab])} ||
{DoFunc, Args} <- FAndAList].
delete_all() ->
ok.
delete_all([], Tab) ->
ets:safe_fixtable(Tab, true),
V = delete_all2(ets:first(Tab), Tab),
ets:safe_fixtable(Tab, false),
V.
delete_all2('$end_of_table', Tab) ->
ok;
delete_all2(Key, Tab) ->
Next = ets:next(Tab, Key),
ets:delete(Tab, Key),
delete_all2(Next, Tab).
insert_rand() ->
ok.
insert_rand([N], Tab) ->
insert_rand([N, N], Tab);
insert_rand([Num, MaxKey], Tab) ->
insert_rand([Num, MaxKey, 1], Tab);
insert_rand([Num, MaxKey, Val], Tab) ->
insert_rand(Num, MaxKey div 4, make_randfun(MaxKey), Val, Tab).
insert_rand(0, Last, RandFun, Val, Tab) ->
ok;
insert_rand(N, Last, RandFun, Val, Tab) ->
New = RandFun(Last),
true = ets:insert(Tab, {New, Val}),
insert_rand(N - 1, New, RandFun, Val, Tab).
traverse_forward() ->
ok.
traverse_forward([], Tab) ->
traverse_forward2(ets:first(Tab), Tab).
traverse_forward2('$end_of_table', Tab) ->
ok;
traverse_forward2(Key, Tab) ->
traverse_forward2(ets:next(Tab, Key), Tab).
%%%
%%% Low-level stuff below here
%%%
do_loop(N, Fun) ->
do_loop_skip(N, 1, 0, Fun).
do_loop_skip(N, Skip, Stop, _) when N =< Stop ->
ok;
do_loop_skip(N, Skip, Stop, Fun) ->
Fun(N),
do_loop_skip(N - Skip, Skip, Stop, Fun).
%%%
%%% smaller_prime()'s algorithm stolen from erl_db.c.
%%%
smaller_prime(N) when N rem 2 == 0 ->
smaller_prime(N - 1);
smaller_prime(N) ->
NModI = smaller_prime_for(3, N),
if NModI * NModI > N -> N;
true -> smaller_prime(N - 2)
end.
smaller_prime_for(I, N) when I * I =< N ->
if N rem I == 0 -> I;
true -> smaller_prime_for(I + 2, N)
end;
smaller_prime_for(I, N) ->
I.
%%% unatomify() can now convert atoms such as '{foo, bar}' and '[1, foo, 3]'
unatomify([]) ->
[];
unatomify([H|T]) when atom(H) ->
L = atom_to_list(H),
[LH|LT] = L,
if
LH >= $0, LH =< $9 ->
[list_to_integer(L) | unatomify(T)];
LH == $- ->
[list_to_integer(L) | unatomify(T)];
LH == ${; LH == $[ ->
case erl_scan:string(L) of
{ok, Tokens, _} ->
case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
{ok, List} when list(List) -> [List | unatomify(T)];
{ok, Term} -> [Term | unatomify(T)];
_ -> [H | unatomify(T)]
end;
_ ->
[H | unatomify(T)]
end;
true ->
[H | unatomify(T)]
end;
unatomify([H|T]) ->
[H | unatomify(T)].
parse_alist([]) ->
[];
parse_alist([Func|T]) ->
case test_defined(Func) of
true ->
{Args, Rest} = parse_alist2(T),
[{Func, Args} | parse_alist(Rest)];
false ->
throw({bad_funcname, Func})
end.
parse_alist2(L) ->
parse_alist2(L, []).
parse_alist2([], Acc) ->
{lists:reverse(Acc), []};
parse_alist2([H|T] = L, Acc) ->
case test_defined(H) of
true ->
{lists:reverse(Acc), L};
false ->
parse_alist2(T, [H|Acc])
end.
test_defined(F) ->
case catch apply(?MODULE, F, []) of
ok ->
true;
_ ->
false
end.
%%% This function is fairly fast and typically hits at least 1/2 of
%%% total space from 0 through N, often more. Careful choices of N
%%% can result in covering all values of 0 through N.
make_randfun(N) ->
SmallerPrime = smaller_prime(N),
YetSmallerPrime = smaller_prime(SmallerPrime - 1),
fun(Old) -> (Old * SmallerPrime) rem YetSmallerPrime end.
format_results([]) ->
ok;
format_results([H|T]) ->
case H of
{Test, {N, ok}} when is_integer(N) ->
io:format("~w ok ~w\n", [Test, N]);
{Test, {N, Huh}} when is_integer(N) ->
io:format("~w FAIL ~w : ~w\n", [Test, N, Huh]);
_ ->
foo
end,
format_results(T).
More information about the erlang-questions
mailing list