From f944953e41a44c2dd443432c3a27b70a9060d1db Mon Sep 17 00:00:00 2001
From: Nikolay Shirokovskiy <nshirokovskiy@tarantool.org>
Date: Tue, 14 May 2024 15:28:34 +0300
Subject: [PATCH] memtx: shutdown memtx gc fiber

As we are going to check for memory leaks in ASAN we need to wait while
memtx gc finishes freeing tuples of dropped primary index or keys of
dropped functional indexes.

Part of #9722

NO_CHANGELOG=internal
NO_DOC=internal
---
 src/box/memtx_engine.cc            | 29 ++++++++++++++++++++++++++---
 test/box-luatest/shutdown_test.lua | 17 +++++++++++++++++
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/src/box/memtx_engine.cc b/src/box/memtx_engine.cc
index c7567f74d5..af07eb96d9 100644
--- a/src/box/memtx_engine.cc
+++ b/src/box/memtx_engine.cc
@@ -88,7 +88,10 @@ static inline struct tuple *
 memtx_tuple_new_raw_impl(struct tuple_format *format, const char *data,
 			 const char *end, bool validate);
 
-template <class ALLOC>
+static void
+memtx_engine_run_gc(struct memtx_engine *memtx, bool *stop);
+
+template<class ALLOC>
 static void
 memtx_alloc_init(void)
 {
@@ -191,6 +194,25 @@ memtx_build_secondary_keys(struct space *space, void *param)
 	return 0;
 }
 
+/**
+ * Memtx shutdown. Shutdown stops all internal fiber. Yields.
+ */
+static void
+memtx_engine_shutdown(struct engine *engine)
+{
+	struct memtx_engine *memtx = (struct memtx_engine *)engine;
+	fiber_cancel(memtx->gc_fiber);
+	fiber_join(memtx->gc_fiber);
+	memtx->gc_fiber = NULL;
+
+#ifdef ENABLE_ASAN
+	/* We check for memory leaks in ASAN. */
+	bool stop = false;
+	while (!stop)
+		memtx_engine_run_gc(memtx, &stop);
+#endif
+}
+
 static void
 memtx_engine_free(struct engine *engine)
 {
@@ -1371,7 +1393,7 @@ memtx_engine_memory_stat(struct engine *engine, struct engine_memory_stat *stat)
 
 static const struct engine_vtab memtx_engine_vtab = {
 	/* .free = */ memtx_engine_free,
-	/* .shutdown = */ generic_engine_shutdown,
+	/* .shutdown = */ memtx_engine_shutdown,
 	/* .create_space = */ memtx_engine_create_space,
 	/* .create_read_view = */ memtx_engine_create_read_view,
 	/* .prepare_join = */ memtx_engine_prepare_join,
@@ -1428,7 +1450,7 @@ memtx_engine_gc_f(va_list va)
 	while (!fiber_is_cancelled()) {
 		FiberGCChecker gc_check;
 		bool stop;
-		ERROR_INJECT_YIELD(ERRINJ_MEMTX_DELAY_GC);
+		ERROR_INJECT_YIELD_CANCELLABLE(ERRINJ_MEMTX_DELAY_GC, return 0);
 		memtx_engine_run_gc(memtx, &stop);
 		if (stop) {
 			fiber_yield_timeout(TIMEOUT_INFINITY);
@@ -1525,6 +1547,7 @@ memtx_engine_new(const char *snap_dirname, bool force_recovery,
 	memtx->gc_fiber = fiber_new_system("memtx.gc", memtx_engine_gc_f);
 	if (memtx->gc_fiber == NULL)
 		goto fail;
+	fiber_set_joinable(memtx->gc_fiber, true);
 
 	/*
 	 * Currently we have two quota consumers: tuple and index allocators.
diff --git a/test/box-luatest/shutdown_test.lua b/test/box-luatest/shutdown_test.lua
index fd02c1a289..636aeeeba0 100644
--- a/test/box-luatest/shutdown_test.lua
+++ b/test/box-luatest/shutdown_test.lua
@@ -171,3 +171,20 @@ g.test_shutdown_of_hanging_client_fiber = function(cg)
     t.assert(cg.server:grep_log('cannot gracefully shutdown client fibers', nil,
              {filename = log}))
 end
+
+-- Let's test we free tuples on index drop and immediate shutdown, before
+-- memtx gc fiber have a chance to free all tuples. That is we take care
+-- too free tuples during shutdown. The actual test is done by LSAN.
+-- This way we make sure other tests in similar situation are not flaky
+-- under ASAN.
+g.test_shutdown_memtx_gc_free_tuples = function(cg)
+    t.tarantool.skip_if_not_debug()
+    cg.server:exec(function()
+        box.schema.create_space('test')
+        box.space.test:create_index('pk')
+        box.space.test:insert{1}
+        box.error.injection.set('ERRINJ_MEMTX_DELAY_GC', true)
+        box.space.test.index.pk:drop()
+    end)
+    server:stop()
+end
-- 
GitLab