diff options
-rw-r--r-- | erts/emulator/beam/erl_db_hash.c | 20 | ||||
-rw-r--r-- | erts/emulator/beam/erl_db_tree.c | 8 | ||||
-rw-r--r-- | erts/emulator/beam/erl_db_util.c | 22 | ||||
-rw-r--r-- | erts/emulator/beam/erl_db_util.h | 3 | ||||
-rw-r--r-- | lib/stdlib/test/ets_SUITE.erl | 114 |
5 files changed, 113 insertions, 54 deletions
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 92ff7de268..73cb58547b 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -1598,12 +1598,18 @@ static int match_traverse(traverse_context_t* ctx, for(;;) { if (*current_ptr != NULL) { if (!is_pseudo_deleted(*current_ptr)) { - match_res = db_match_dbterm(&tb->common, ctx->p, mpi.mp, - &(*current_ptr)->dbterm, hpp, 2); + DbTerm* obj = &(*current_ptr)->dbterm; + if (tb->common.compress) + obj = db_alloc_tmp_uncompressed(&tb->common, obj); + match_res = db_match_dbterm_uncompressed(&tb->common, ctx->p, mpi.mp, + obj, hpp, 2); saved_current = *current_ptr; if (ctx->on_match_res(ctx, slot_ix, ¤t_ptr, match_res)) { ++got; } + if (tb->common.compress) + db_free_tmp_uncompressed(obj); + --iterations_left; if (*current_ptr != saved_current) { /* Don't advance to next, the callback did it already */ @@ -1717,12 +1723,18 @@ static int match_traverse_continue(traverse_context_t* ctx, for(;;) { if (*current_ptr != NULL) { if (!is_pseudo_deleted(*current_ptr)) { - match_res = db_match_dbterm(&tb->common, ctx->p, *mpp, - &(*current_ptr)->dbterm, hpp, 2); + DbTerm* obj = &(*current_ptr)->dbterm; + if (tb->common.compress) + obj = db_alloc_tmp_uncompressed(&tb->common, obj); + match_res = db_match_dbterm_uncompressed(&tb->common, ctx->p, *mpp, + obj, hpp, 2); saved_current = *current_ptr; if (ctx->on_match_res(ctx, slot_ix, ¤t_ptr, match_res)) { ++got; } + if (tb->common.compress) + db_free_tmp_uncompressed(obj); + --iterations_left; if (*current_ptr != saved_current) { /* Don't advance to next, the callback did it already */ diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 131ec123b0..90e37c4b4e 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -4161,6 +4161,7 @@ static int doit_select_replace(DbTableCommon *tb, TreeDbTerm **this, int forward) { struct select_replace_context *sc = (struct select_replace_context *) ptr; + DbTerm* obj; Eterm ret; sc->lastobj = (*this)->dbterm.tpl; @@ -4171,7 +4172,10 @@ static int doit_select_replace(DbTableCommon *tb, TreeDbTerm **this, GETKEY_WITH_POS(sc->keypos, (*this)->dbterm.tpl)) > 0)) { return 0; } - ret = db_match_dbterm(tb, sc->p, sc->mp, &(*this)->dbterm, NULL, 0); + obj = &(*this)->dbterm; + if (tb->compress) + obj = db_alloc_tmp_uncompressed(tb, obj); + ret = db_match_dbterm_uncompressed(tb, sc->p, sc->mp, obj, NULL, 0); if (is_value(ret)) { TreeDbTerm* new; @@ -4190,6 +4194,8 @@ static int doit_select_replace(DbTableCommon *tb, TreeDbTerm **this, free_term((DbTable*)tb, old); ++(sc->replaced); } + if (tb->compress) + db_free_tmp_uncompressed(obj); if (--(sc->max) <= 0) { return 0; } diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index a0be53c896..2087e1c337 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -953,8 +953,6 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace); static Eterm seq_trace_fake(Process *p, Eterm arg1); -static void db_free_tmp_uncompressed(DbTerm* obj); - /* ** Interface routines. @@ -5410,17 +5408,13 @@ void db_free_tmp_uncompressed(DbTerm* obj) erts_free(ERTS_ALC_T_TMP, obj); } -Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, - DbTerm* obj, Eterm** hpp, Uint extra) +Eterm db_match_dbterm_uncompressed(DbTableCommon* tb, Process* c_p, Binary* bprog, + DbTerm* obj, Eterm** hpp, Uint extra) { enum erts_pam_run_flags flags; Uint32 dummy; Eterm res; - if (tb->compress) { - obj = db_alloc_tmp_uncompressed(tb, obj); - } - flags = (hpp ? ERTS_PAM_COPY_RESULT | ERTS_PAM_CONTIGUOUS_TUPLE : ERTS_PAM_TMP_RESULT | ERTS_PAM_CONTIGUOUS_TUPLE); @@ -5432,9 +5426,19 @@ Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, if (is_value(res) && hpp!=NULL) { *hpp = HAlloc(c_p, extra); } + return res; +} +Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, + DbTerm* obj, Eterm** hpp, Uint extra) +{ + Eterm res; + if (tb->compress) { + obj = db_alloc_tmp_uncompressed(tb, obj); + } + res = db_match_dbterm_uncompressed(tb, c_p, bprog, obj, hpp, extra); if (tb->compress) { - db_free_tmp_uncompressed(obj); + db_free_tmp_uncompressed(obj); } return res; } diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h index 8049d40eff..86170677ba 100644 --- a/erts/emulator/beam/erl_db_util.h +++ b/erts/emulator/beam/erl_db_util.h @@ -373,6 +373,7 @@ Eterm db_copy_from_comp(DbTableCommon* tb, DbTerm* bp, Eterm** hpp, ErlOffHeap* off_heap); int db_eq_comp(DbTableCommon* tb, Eterm a, DbTerm* b); DbTerm* db_alloc_tmp_uncompressed(DbTableCommon* tb, DbTerm* org); +void db_free_tmp_uncompressed(DbTerm* obj); ERTS_GLB_INLINE Eterm db_copy_object_from_ets(DbTableCommon* tb, DbTerm* bp, Eterm** hpp, ErlOffHeap* off_heap); @@ -543,6 +544,8 @@ Binary *db_match_compile(Eterm *matchexpr, Eterm *guards, Uint *freasonp); /* Returns newly allocated MatchProg binary with refc == 0*/ +Eterm db_match_dbterm_uncompressed(DbTableCommon* tb, Process* c_p, Binary* bprog, + DbTerm* obj, Eterm** hpp, Uint extra); Eterm db_match_dbterm(DbTableCommon* tb, Process* c_p, Binary* bprog, DbTerm* obj, Eterm** hpp, Uint extra); diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 9e5410c10f..08cedf704d 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -1653,7 +1653,11 @@ t_select_delete(Config) when is_list(Config) -> %% Tests the ets:select_replace/2 BIF t_select_replace(Config) when is_list(Config) -> EtsMem = etsmem(), - Tables = fill_sets_int(10000) ++ fill_sets_int(10000, [{write_concurrency,true}]), + repeat_for_opts(fun do_select_replace/1), + verify_etsmem(EtsMem). + +do_select_replace(Opts) -> + Tables = fill_sets_intup(10000, Opts), TestFun = fun (Table, TableType) when TableType =:= bag -> % Operation not supported; bag implementation @@ -1662,80 +1666,80 @@ t_select_replace(Config) when is_list(Config) -> (Table, TableType) -> % Invalid replacement doesn't keep the key - MatchSpec1 = [{{'$1', '$2'}, + MatchSpec1 = [{{{'$1','$3'}, '$2'}, [{'=:=', {'band', '$1', 2#11}, 2#11}, {'=/=', {'hd', '$2'}, $x}], - [{{'$2', '$1'}}]}], + [{{{{'$2','$3'}}, '$1'}}]}], {'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec1)), % Invalid replacement doesn't keep the key (even though it would be the same value) - MatchSpec2 = [{{'$1', '$2'}, + MatchSpec2 = [{{{'$1','$3'}, '$2'}, [{'=:=', {'band', '$1', 2#11}, 2#11}], - [{{{'+', '$1', 0}, '$2'}}]}, - {{'$1', '$2'}, + [{{{{{'+', '$1', 0},'$3'}}, '$2'}}]}, + {{{'$1','$3'}, '$2'}, [{'=/=', {'band', '$1', 2#11}, 2#11}], - [{{{'-', '$1', 0}, '$2'}}]}], + [{{{{{'-', '$1', 0},'$3'}}, '$2'}}]}], {'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec2)), % Invalid replacement changes key to float equivalent - MatchSpec3 = [{{'$1', '$2'}, + MatchSpec3 = [{{{'$1','$3'}, '$2'}, [{'=:=', {'band', '$1', 2#11}, 2#11}, {'=/=', {'hd', '$2'}, $x}], - [{{{'*', '$1', 1.0}, '$2'}}]}], + [{{{{{'*', '$1', 1.0},'$3'}}, '$2'}}]}], {'EXIT',{badarg,_}} = (catch ets:select_replace(Table, MatchSpec3)), % Replacements are differently-sized tuples - MatchSpec4_A = [{{'$1','$2'}, + MatchSpec4_A = [{{{'$1','$3'},'$2'}, [{'<', {'rem', '$1', 5}, 2}], - [{{'$1', [$x | '$2'], stuff}}]}], - MatchSpec4_B = [{{'$1','$2','_'}, + [{{{{'$1','$3'}}, [$x | '$2'], stuff}}]}], + MatchSpec4_B = [{{{'$1','$3'},'$2','_'}, [], - [{{'$1','$2'}}]}], + [{{{{'$1','$3'}},'$2'}}]}], 4000 = ets:select_replace(Table, MatchSpec4_A), 4000 = ets:select_replace(Table, MatchSpec4_B), % Replacement is the same tuple - MatchSpec5 = [{{'$1', '$2'}, + MatchSpec5 = [{{{'$1','$3'}, '$2'}, [{'>', {'rem', '$1', 5}, 3}], ['$_']}], 2000 = ets:select_replace(Table, MatchSpec5), % Replacement reconstructs an equal tuple - MatchSpec6 = [{{'$1', '$2'}, + MatchSpec6 = [{{{'$1','$3'}, '$2'}, [{'>', {'rem', '$1', 5}, 3}], - [{{'$1', '$2'}}]}], + [{{{{'$1','$3'}}, '$2'}}]}], 2000 = ets:select_replace(Table, MatchSpec6), % Replacement uses {element,KeyPos,T} for key 2000 = ets:select_replace(Table, - [{{'$1', '$2'}, + [{{{'$1','$3'}, '$2'}, [{'>', {'rem', '$1', 5}, 3}], [{{{element, 1, '$_'}, '$2'}}]}]), % Replacement uses wrong {element,KeyPos,T} for key {'EXIT',{badarg,_}} = (catch ets:select_replace(Table, - [{{'$1', '$2'}, + [{{{'$1','$3'}, '$2'}, [], [{{{element, 2, '$_'}, '$2'}}]}])), check(Table, - fun ({N, [$x, C | _]}) when ((N rem 5) < 2) -> (C >= $0) andalso (C =< $9); - ({N, [C | _]}) when is_float(N) -> (C >= $0) andalso (C =< $9); - ({N, [C | _]}) when ((N rem 5) > 3) -> (C >= $0) andalso (C =< $9); + fun ({{N,_}, [$x, C | _]}) when ((N rem 5) < 2) -> (C >= $0) andalso (C =< $9); + ({{N,_}, [C | _]}) when is_float(N) -> (C >= $0) andalso (C =< $9); + ({{N,_}, [C | _]}) when ((N rem 5) > 3) -> (C >= $0) andalso (C =< $9); ({_, [C | _]}) -> (C >= $0) andalso (C =< $9) end, 10000), % Replace unbound range (>) - MatchSpec7 = [{{'$1', '$2'}, + MatchSpec7 = [{{{'$1','$3'}, '$2'}, [{'>', '$1', 7000}], - [{{'$1', {{gt_range, '$2'}}}}]}], + [{{{{'$1','$3'}}, {{gt_range, '$2'}}}}]}], 3000 = ets:select_replace(Table, MatchSpec7), % Replace unbound range (<) - MatchSpec8 = [{{'$1', '$2'}, + MatchSpec8 = [{{{'$1','$3'}, '$2'}, [{'<', '$1', 3000}], - [{{'$1', {{le_range, '$2'}}}}]}], + [{{{{'$1','$3'}}, {{le_range, '$2'}}}}]}], case TableType of ordered_set -> 2999 = ets:select_replace(Table, MatchSpec8); set -> 2999 = ets:select_replace(Table, MatchSpec8); @@ -1743,10 +1747,10 @@ t_select_replace(Config) when is_list(Config) -> end, % Replace bound range - MatchSpec9 = [{{'$1', '$2'}, + MatchSpec9 = [{{{'$1','$3'}, '$2'}, [{'>=', '$1', 3001}, {'<', '$1', 7000}], - [{{'$1', {{range, '$2'}}}}]}], + [{{{{'$1','$3'}}, {{range, '$2'}}}}]}], case TableType of ordered_set -> 3999 = ets:select_replace(Table, MatchSpec9); set -> 3999 = ets:select_replace(Table, MatchSpec9); @@ -1754,12 +1758,12 @@ t_select_replace(Config) when is_list(Config) -> end, % Replace particular keys - MatchSpec10 = [{{'$1', '$2'}, + MatchSpec10 = [{{{'$1','$3'}, '$2'}, [{'==', '$1', 3000}], - [{{'$1', {{specific1, '$2'}}}}]}, - {{'$1', '$2'}, + [{{{{'$1','$3'}}, {{specific1, '$2'}}}}]}, + {{{'$1','$3'}, '$2'}, [{'==', '$1', 7000}], - [{{'$1', {{specific2, '$2'}}}}]}], + [{{{{'$1','$3'}}, {{specific2, '$2'}}}}]}], case TableType of ordered_set -> 2 = ets:select_replace(Table, MatchSpec10); set -> 2 = ets:select_replace(Table, MatchSpec10); @@ -1767,11 +1771,11 @@ t_select_replace(Config) when is_list(Config) -> end, check(Table, - fun ({N, {gt_range, _}}) -> N > 7000; - ({N, {le_range, _}}) -> N < 3000; - ({N, {range, _}}) -> (N >= 3001) andalso (N < 7000); - ({N, {specific1, _}}) -> N == 3000; - ({N, {specific2, _}}) -> N == 7000 + fun ({{N,_}, {gt_range, _}}) -> N > 7000; + ({{N,_}, {le_range, _}}) -> N < 3000; + ({{N,_}, {range, _}}) -> (N >= 3001) andalso (N < 7000); + ({{N,_}, {specific1, _}}) -> N == 3000; + ({{N,_}, {specific2, _}}) -> N == 7000 end, 10000), @@ -1811,7 +1815,7 @@ t_select_replace(Config) when is_list(Config) -> ] end, - T2 = ets:new(x, []), + T2 = ets:new(x, Opts), [lists:foreach(fun({A, B}) -> %% just check that matchspec is accepted 0 = ets:select_replace(T2, [{{A, '$2', '$3'}, [], [{{B, '$3', '$2'}}]}]) @@ -1872,8 +1876,7 @@ t_select_replace(Config) when is_list(Config) -> ets:delete(T2), - - verify_etsmem(EtsMem). + ok. %% OTP-15346: Bug caused select_replace of bound key to corrupt static stack %% used by ets:next and ets:prev. @@ -5902,6 +5905,7 @@ make_table(Name, Options, Elements) -> T = ets_new(Name, Options), lists:foreach(fun(E) -> ets:insert(T, E) end, Elements), T. + filltabint(Tab,0) -> Tab; filltabint(Tab,N) -> @@ -5929,6 +5933,22 @@ xfilltabint(Tab,N) -> filltabint(Tab,N) end. +filltabintup(Tab,0) -> + Tab; +filltabintup(Tab,N) -> + ets:insert(Tab,{{N,integer_to_list(N)},integer_to_list(N)}), + filltabintup(Tab,N-1). + +filltabintup2(Tab,0) -> + Tab; +filltabintup2(Tab,N) -> + ets:insert(Tab,{{N + N rem 2,integer_to_list(N)},integer_to_list(N)}), + filltabintup2(Tab,N-1). +filltabintup3(Tab,0) -> + Tab; +filltabintup3(Tab,N) -> + ets:insert(Tab,{{N + N rem 2,integer_to_list(N + N rem 2)},integer_to_list(N + N rem 2)}), + filltabintup3(Tab,N-1). filltabstr(Tab,N) -> filltabstr(Tab,0,N). @@ -5974,6 +5994,19 @@ fill_sets_int(N,Opts) -> filltabint3(Tab4,N), [Tab1,Tab2,Tab3,Tab4]. +fill_sets_intup(N) -> + fill_sets_int(N,[]). +fill_sets_intup(N,Opts) -> + Tab1 = ets_new(xxx, [ordered_set|Opts]), + filltabintup(Tab1,N), + Tab2 = ets_new(xxx, [set|Opts]), + filltabintup(Tab2,N), + Tab3 = ets_new(xxx, [bag|Opts]), + filltabintup2(Tab3,N), + Tab4 = ets_new(xxx, [duplicate_bag|Opts]), + filltabintup3(Tab4,N), + [Tab1,Tab2,Tab3,Tab4]. + check_fun(_Tab,_Fun,'$end_of_table') -> ok; check_fun(Tab,Fun,Item) -> @@ -6941,7 +6974,8 @@ smp_select_delete_do(Opts) -> smp_select_replace(Config) when is_list(Config) -> repeat_for_opts(fun smp_select_replace_do/1, - [[set,ordered_set,stim_cat_ord_set,duplicate_bag]]). + [[set,ordered_set,stim_cat_ord_set,duplicate_bag], + compressed]). smp_select_replace_do(Opts) -> KeyRange = 20, |