summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSverker Eriksson <sverker@erlang.org>2018-11-01 16:53:06 +0100
committerSverker Eriksson <sverker@erlang.org>2018-11-01 17:48:20 +0100
commite5b006ce3361467f77c372a4b6f19f9e0d9ddf23 (patch)
tree62b1b9d5c3d9aeb76b8f876b2a3b284f4c59cebe
parentcc18836780d7d047bf53b1ff8d94a6b31b58f98a (diff)
downloaderlang-e5b006ce3361467f77c372a4b6f19f9e0d9ddf23.tar.gz
erts: Fix bug for catree iteration
with keys containing off-heap terms. The passed key may actually be the one already saved (if nodes have been joined), in which case we do nothing. Calling destroy_route_key() may destroy off-heap data.
-rw-r--r--erts/emulator/beam/erl_db_catree.c3
-rw-r--r--lib/stdlib/test/ets_SUITE.erl24
2 files changed, 16 insertions, 11 deletions
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index b52a4a53fe..7a5587f1fd 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -1585,7 +1585,8 @@ static Eterm copy_iter_search_key(CATreeRootIterator* iter, Eterm key)
return key;
if (iter->search_key) {
- ASSERT(key != iter->search_key->term);
+ if (key == iter->search_key->term)
+ return key; /* already saved */
destroy_route_key(iter->search_key);
}
key_size = size_object(key);
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index e49181b12f..cc369979f7 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -6052,8 +6052,9 @@ smp_ordered_iteration(Config) when is_list(Config) ->
smp_ordered_iteration_do(Opts) ->
KeyRange = 1000,
+ OffHeap = fun() -> dummy end, % To exercise key copy/destroy code.
KeyFun = fun(K, Type) ->
- {K div 10, K rem 10, Type}
+ {K div 10, K rem 10, Type, OffHeap}
end,
StimKeyFun = fun(K) ->
KeyFun(K, element(rand:uniform(3),
@@ -6084,7 +6085,7 @@ smp_ordered_iteration_do(Opts) ->
incr_counter(select_delete_bk, Counters);
R when R =< 20 ->
%% Delete partially bound key
- ets:select_delete(T, [{{{K div 10, '_', volatile}, '_'}, [], [true]}]),
+ ets:select_delete(T, [{{{K div 10, '_', volatile, '_'}, '_'}, [], [true]}]),
incr_counter(select_delete_pbk, Counters);
R when R =< 21 ->
%% Replace bound key
@@ -6093,7 +6094,7 @@ smp_ordered_iteration_do(Opts) ->
incr_counter(select_replace_bk, Counters);
_ ->
%% Replace partially bound key
- ets:select_replace(T, [{{{K div 10, '_', volatile}, '$1'}, [],
+ ets:select_replace(T, [{{{K div 10, '_', volatile, '_'}, '$1'}, [],
[{{{element,1,'$_'}, {'+','$1',1}}}]}]),
incr_counter(select_replace_pbk, Counters)
end,
@@ -6106,14 +6107,17 @@ smp_ordered_iteration_do(Opts) ->
FiniF = fun (Acc) -> Acc end,
Pids = run_sched_workers(InitF, ExecF, FiniF, infinite),
timer:send_after(1000, stop),
+
+ Log2ChunkMax = math:log2(NStable*2),
Rounds = fun Loop(N) ->
- NStable = ets:select_count(T, [{{{'_', '_', stable}, '_'}, [], [true]}]),
+ MS = [{{{'_', '_', stable, '_'}, '_'}, [], [true]}],
+ NStable = ets:select_count(T, MS),
NStable = count_stable(T, next, ets:first(T), 0),
NStable = count_stable(T, prev, ets:last(T), 0),
- NStable = length(ets:select(T, [{{{'_', '_', stable}, '_'}, [], [true]}])),
- NStable = length(ets:select_reverse(T, [{{{'_', '_', stable}, '_'}, [], [true]}])),
- NStable = ets_select_chunks_count(T, [{{{'_', '_', stable}, '_'}, [], [true]}],
- rand:uniform(5)),
+ NStable = length(ets:select(T, MS)),
+ NStable = length(ets:select_reverse(T, MS)),
+ Chunk = round(math:pow(2, rand:uniform()*Log2ChunkMax)),
+ NStable = ets_select_chunks_count(T, MS, Chunk),
receive stop -> N
after 0 -> Loop(N+1)
end
@@ -6130,9 +6134,9 @@ smp_ordered_iteration_do(Opts) ->
incr_counter(Name, Counters) ->
Counters#{Name => maps:get(Name, Counters, 0) + 1}.
-count_stable(T, Next, {_, _, stable}=Key, N) ->
+count_stable(T, Next, {_, _, stable, _}=Key, N) ->
count_stable(T, Next, ets:Next(T, Key), N+1);
-count_stable(T, Next, {_, _, volatile}=Key, N) ->
+count_stable(T, Next, {_, _, volatile, _}=Key, N) ->
count_stable(T, Next, ets:Next(T, Key), N);
count_stable(_, _, '$end_of_table', N) ->
N.