From 5fccca163c41195b1d022be77e06d6b80291240a Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Mon, 2 Oct 2023 16:59:38 +0200 Subject: [PATCH] erts: Fix db_match hashmap copy bug --- erts/emulator/beam/erl_db_util.c | 69 +++++++++++++++++++++++++++----- lib/stdlib/test/ets_SUITE.erl | 25 ++++++++++++ 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index 60e7b46d7f81..f77845a5e521 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -4031,10 +4031,12 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, return retOk; } else { DECLARE_WSTACK(wstack); + DMC_STACK_TYPE(UWord) instr_save; Eterm *kv; int c; int textpos = DMC_STACK_NUM(*text); int stackpos = context->stack_used; + int preventive_bumps = 0; ASSERT(is_hashmap(t)); @@ -4048,16 +4050,32 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, DESTROY_WSTACK(wstack); return ret; } - if (!c) + + if (!c) { constant_values = 0; + break; + } + + ++context->stack_used; + ++preventive_bumps; + if ((ret = dmc_expr(context, heap, text, CDR(kv), &c)) != retOk) { DESTROY_WSTACK(wstack); return ret; } - if (!c) + + if (!c) { constant_values = 0; + break; + } + + ++context->stack_used; + ++preventive_bumps; + } + context->stack_used -= preventive_bumps; + if (constant_values) { ASSERT(DMC_STACK_NUM(*text) == textpos); *constant = 1; @@ -4065,35 +4083,64 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, return retOk; } - /* reset the program to the original position and re-emit everything */ - DMC_STACK_NUM(*text) = textpos; - context->stack_used = stackpos; - - *constant = 0; + DMC_INIT_STACK(instr_save); + while (DMC_STACK_NUM(*text) > textpos) { + DMC_PUSH(instr_save, DMC_POP(*text)); + } hashmap_iterator_init(&wstack, t, 1); while ((kv=hashmap_iterator_prev(&wstack)) != NULL) { + do_emit_constant(context, text, CAR(kv)); + if (--preventive_bumps == 0) { + while(!DMC_EMPTY(instr_save)) { + DMC_PUSH(*text, DMC_POP(instr_save)); + } + break; + } + do_emit_constant(context, text, CDR(kv)); + if (--preventive_bumps == 0) { + while(!DMC_EMPTY(instr_save)) { + DMC_PUSH(*text, DMC_POP(instr_save)); + } + kv=hashmap_iterator_prev(&wstack); + if ((ret = dmc_expr(context, heap, text, CDR(kv), &c)) != retOk) { + DESTROY_WSTACK(wstack); + return ret; + } + if (c) { + do_emit_constant(context, text, CDR(kv)); + } + break; + } + } + + while ((kv=hashmap_iterator_prev(&wstack)) != NULL) { + /* push key */ if ((ret = dmc_expr(context, heap, text, CAR(kv), &c)) != retOk) { - DESTROY_WSTACK(wstack); - return ret; - } + DESTROY_WSTACK(wstack); + return ret; + } + if (c) { do_emit_constant(context, text, CAR(kv)); } - + /* push value */ if ((ret = dmc_expr(context, heap, text, CDR(kv), &c)) != retOk) { DESTROY_WSTACK(wstack); return ret; } + if (c) { do_emit_constant(context, text, CDR(kv)); } } + ASSERT(preventive_bumps <= 0); DMC_PUSH2(*text, matchMkHashMap, nelems); context->stack_used -= 2*nelems - 1; /* n keys & values => 1 map */ + DMC_FREE(instr_save); DESTROY_WSTACK(wstack); return retOk; } diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index f875c77d49a1..7ee046058204 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -1938,6 +1938,31 @@ t_select_flatmap_term_copy_bug(_Config) -> ets:delete(T), ok. +t_select_hashmap_term_copy_bug(_Config) -> + + T = ets:new(a,[]), + ets:insert(T, {list_to_binary(lists:duplicate(36,$a)), + list_to_binary(lists:duplicate(36,$b)), + list_to_binary(lists:duplicate(36,$c))}), + V1 = ets:select(T, [{{'$1','$2','$3'},[],[#{ '$1' => a }]}]), + erlang:garbage_collect(), + V1 = ets:select(T, [{{'$1','$2','$3'},[],[#{ '$1' => a }]}]), + erlang:garbage_collect(), + V2 = ets:select(T, [{{'$1','$2','$3'},[],[#{ a => '$1' }]}]), + erlang:garbage_collect(), + V2 = ets:select(T, [{{'$1','$2','$3'},[],[#{ a => '$1' }]}]), + erlang:garbage_collect(), + V3 = ets:select(T, [{{'$1','$2','$3'},[],[#{ '$1' => '$1' }]}]), + erlang:garbage_collect(), + V3 = ets:select(T, [{{'$1','$2','$3'},[],[#{ '$1' => '$1' }]}]), + erlang:garbage_collect(), + V3 = ets:select(T, [{{'$1','$2','$3'},[],[#{ a => a }]}]), + erlang:garbage_collect(), + V3 = ets:select(T, [{{'$1','$2','$3'},[],[#{ a => a }]}]), + erlang:garbage_collect(), + ets:delete(T), + ok. + %% Test that partly bound keys gives faster matches. partly_bound(Config) when is_list(Config) -> case os:type() of