File 0516-erts-Fix-ets-rename-during-ets-delete_all_objects.patch of Package erlang
From 423690db5488fb1c08ea29c2017109edcab76266 Mon Sep 17 00:00:00 2001
From: Sverker Eriksson <sverker@erlang.org>
Date: Tue, 11 Oct 2022 20:27:58 +0200
Subject: [PATCH 2/2] erts: Fix ets:rename during ets:delete_all_objects
Concurrent ets:rename could cause ets:delete_all_objects
to fail halfway through with badarg
"the table identifier does not refer to an existing ETS table"
---
erts/emulator/beam/erl_db.c | 10 ++++++-
erts/emulator/beam/erl_db_util.h | 2 +-
lib/stdlib/test/ets_SUITE.erl | 48 +++++++++++++++++++++-----------
3 files changed, 42 insertions(+), 18 deletions(-)
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 05b21c53e2..db4eb95034 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -2832,15 +2832,23 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2)
* the table and instead pitch in deleting objects
* (in delete_all_objects_continue) and then trap to self.
*/
+ Eterm tid;
ASSERT((tb->common.status & (DB_PRIVATE|DB_PROTECTED|DB_PUBLIC))
==
(tb->common.type & (DB_PRIVATE|DB_PROTECTED|DB_PUBLIC)));
tb->common.status &= ~(DB_PRIVATE|DB_PROTECTED|DB_PUBLIC);
tb->common.status |= DB_BUSY;
db_unlock(tb, LCK_WRITE);
+
+ if (is_atom(BIF_ARG_1)) {
+ ASSERT(is_table_named(tb));
+ tid = make_tid(BIF_P, tb);
+ } else {
+ tid = BIF_ARG_1;
+ }
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(BIF_TRAP_EXPORT(BIF_ets_internal_delete_all_2), BIF_P,
- BIF_ARG_1, nitems_holder);
+ tid, nitems_holder);
}
else {
/* Done, no trapping needed */
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 6ed1e15104..a57febffdb 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -302,7 +302,7 @@ typedef struct db_table_common {
UWord heir_data; /* To send in ETS-TRANSFER (is_immed or (DbTerm*) */
Uint64 heir_started_interval; /* To further identify the heir */
Eterm the_name; /* an atom */
- Binary *btid;
+ Binary *btid; /* table magic ref, read only after creation */
DbTableMethod* meth; /* table methods */
/* The ErtsFlxCtr below contains:
* - Total number of items in table
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index a1542247c3..615511471a 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -42,7 +42,7 @@
-export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]).
-export([t_delete_object/1, t_init_table/1, t_whitebox/1,
select_bound_chunk/1, t_delete_all_objects/1, t_test_ms/1,
- t_delete_all_objects_trap_unfix/1,
+ t_delete_all_objects_trap/1,
t_select_delete/1,t_select_replace/1,t_select_replace_next_bug/1,
t_select_pam_stack_overflow_bug/1,
t_select_flatmap_term_copy_bug/1,
@@ -150,7 +150,7 @@ all() ->
match_heavy, {group, fold}, member, t_delete_object,
select_bound_chunk,
t_init_table, t_whitebox, t_delete_all_objects,
- t_delete_all_objects_trap_unfix,
+ t_delete_all_objects_trap,
t_test_ms, t_select_delete, t_select_replace,
t_select_replace_next_bug,
t_select_pam_stack_overflow_bug,
@@ -1041,30 +1041,43 @@ inserter(T, Next, Papa) ->
end.
-%% Test unfix during delete_all_objects
-t_delete_all_objects_trap_unfix(Config) when is_list(Config) ->
+%% Poke table during delete_all_objects
+t_delete_all_objects_trap(Config) when is_list(Config) ->
EtsMem = etsmem(),
repeat_for_opts_all_set_table_types(
fun(Opts) ->
- delete_all_objects_trap_unfix(Opts, unfix),
- delete_all_objects_trap_unfix(Opts, exit)
+ delete_all_objects_trap(Opts, unfix),
+ delete_all_objects_trap(Opts, exit),
+ delete_all_objects_trap(Opts, rename)
end),
verify_etsmem(EtsMem),
ok.
-delete_all_objects_trap_unfix(Opts, Mode) ->
+delete_all_objects_trap(Opts, Mode) ->
io:format("Opts = ~p\nMode = ~p\n", [Opts, Mode]),
Tester = self(),
KeyRange = 50_000,
- T=ets_new(x, Opts, KeyRange),
+ TableName = delete_all_objects_trap,
+ {Tref,T} =
+ case Mode of
+ rename ->
+ TableName = ets_new(TableName, [named_table,public|Opts], KeyRange),
+ {ets:whereis(TableName), TableName};
+ _ ->
+ Tid = ets_new(x, Opts, KeyRange),
+ {Tid,Tid}
+ end,
filltabint(T, KeyRange),
KeyRange = ets:info(T,size),
FixerFun =
fun() ->
erlang:trace(Tester, true, [running]),
- ets:safe_fixtable(T, true),
+ case Mode of
+ rename -> ok;
+ _ -> ets:safe_fixtable(T, true)
+ end,
io:format("Wait for ets:delete_all_objects/1 to yield...\n", []),
- Tester ! {safe_fixtable, self()},
+ Tester ! {ready, self()},
repeat_while(
fun() ->
case receive_any() of
@@ -1083,18 +1096,21 @@ delete_all_objects_trap_unfix(Opts, Mode) ->
ets:safe_fixtable(T, false);
exit ->
%%io:format("Exit and do auto-unfix...\n",[]),
- exit
+ exit;
+ rename ->
+ %%io:format("Rename table...\n",[]),
+ renamed = ets:rename(T, renamed)
end
end,
{Fixer, Mon} = spawn_opt(FixerFun, [link, monitor]),
- {safe_fixtable, Fixer} = receive_any(),
+ {ready, Fixer} = receive_any(),
true = ets:delete_all_objects(T),
Fixer ! "delete_all_objects done",
- 0 = ets:info(T,size),
+ 0 = ets:info(Tref,size),
{'DOWN', Mon, process, Fixer, normal} = receive_any(),
- 0 = get_kept_objects(T),
- false = ets:info(T,safe_fixed),
- ets:delete(T),
+ 0 = get_kept_objects(Tref),
+ false = ets:info(Tref,safe_fixed),
+ ets:delete(Tref),
ok.
--
2.35.3