From 9bfea8d23325476ea32d593c1db0f4c969cf3a8d Mon Sep 17 00:00:00 2001
From: Alexandr Lyapunov <a.lyapunov@corp.mail.ru>
Date: Thu, 9 Jul 2015 19:10:01 +0300
Subject: [PATCH] fixed gh-731 : now every rtree has it's own matras instance
 that is destroyed with rtree, releasing all memory of tree instance

---
 src/box/memtx_rtree.cc   | 43 ++--------------------------------------
 src/lib/salad/rtree.c    | 34 ++++++++++++++++++++++---------
 src/lib/salad/rtree.h    | 23 +++++++++++----------
 test/unit/CMakeLists.txt |  4 ++--
 test/unit/rtree.cc       | 18 +++++++++--------
 test/unit/rtree_itr.cc   | 42 +++++++++++++++++++++++----------------
 6 files changed, 77 insertions(+), 87 deletions(-)

diff --git a/src/box/memtx_rtree.cc b/src/box/memtx_rtree.cc
index 7106aa0f7c..4f5e414951 100644
--- a/src/box/memtx_rtree.cc
+++ b/src/box/memtx_rtree.cc
@@ -34,11 +34,6 @@
 #include "fiber.h"
 #include "small/small.h"
 
-/**
- * Single-linked list of free rtree pages
- */
-static void *rtree_free_pages = 0;
-
 /* {{{ Utilities. *************************************************/
 
 inline void extract_rectangle(struct rtree_rect *rect,
@@ -122,41 +117,6 @@ index_rtree_iterator_next(struct iterator *i)
 
 /* {{{ MemtxRTree  **********************************************************/
 
-static void *
-rtree_page_alloc()
-{
-	ERROR_INJECT(ERRINJ_INDEX_ALLOC, return 0);
-	if (!rtree_free_pages) {
-		/**
-		 * No free pages in list - let's allocate new extent, split it
-		 * into pages and add them to the list.
-		 */
-		char *extent = (char *)memtx_index_extent_alloc();
-		if (!extent) {
-			panic("%s", "Memory allocation failed in rtree");
-			return 0;
-		}
-		assert(MEMTX_EXTENT_SIZE % RTREE_PAGE_SIZE == 0);
-		assert(RTREE_PAGE_SIZE >= sizeof(void *));
-		for (size_t i = 0; i < MEMTX_EXTENT_SIZE; i += RTREE_PAGE_SIZE) {
-			*(void **)(extent + i) = rtree_free_pages;
-			rtree_free_pages = (void *)(extent + i);
-		}
-	}
-	/* Now we surely have a free page in free list */
-	void *res = rtree_free_pages;
-	rtree_free_pages = *(void **)rtree_free_pages;
-	return res;
-}
-
-static void
-rtree_page_free(void *page)
-{
-	/* Just add to free list. */
-	*(void **)page = rtree_free_pages;
-	rtree_free_pages = page;
-}
-
 MemtxRTree::~MemtxRTree()
 {
 	// Iterator has to be destroyed prior to tree
@@ -175,7 +135,8 @@ MemtxRTree::MemtxRTree(struct key_def *key_def)
 	assert(key_def->is_unique == false);
 
 	memtx_index_arena_init();
-	rtree_init(&tree, rtree_page_alloc, rtree_page_free);
+	rtree_init(&tree, MEMTX_EXTENT_SIZE,
+		   memtx_index_extent_alloc, memtx_index_extent_free);
 }
 
 size_t
diff --git a/src/lib/salad/rtree.c b/src/lib/salad/rtree.c
index a14f89134e..70a43d410c 100644
--- a/src/lib/salad/rtree.c
+++ b/src/lib/salad/rtree.c
@@ -249,13 +249,23 @@ rtree_always_true(const struct rtree_rect *rt1,
 static struct rtree_page *
 rtree_alloc_page(struct rtree *tree)
 {
-	return (struct rtree_page *)tree->page_alloc();
+	if (tree->free_pages) {
+		struct rtree_page *result =
+			(struct rtree_page *)tree->free_pages;
+		tree->free_pages = *(void **)tree->free_pages;
+		return result;
+	} else {
+		uint32_t unused_id;
+		return (struct rtree_page *)
+			matras_alloc(&tree->mtab, &unused_id);
+	}
 }
 
 static void
 rtree_free_page(struct rtree *tree, struct rtree_page *page)
 {
-	tree->page_free(page);
+	*(void **)page = tree->free_pages;
+	tree->free_pages = (void *)page;
 }
 
 static void
@@ -558,7 +568,7 @@ rtree_page_purge(struct rtree *tree, struct rtree_page *page, int level)
 		for (int i = 0; i < page->n; i++)
 			rtree_page_purge(tree, page->b[i].data.page, level);
 	}
-	tree->page_free(page);
+	rtree_free_page(tree, page);
 }
 
 /*------------------------------------------------------------------------- */
@@ -624,7 +634,8 @@ rtree_iterator_destroy(struct rtree_iterator *itr)
 	struct rtree_neighbor_page *curr, *next;
 	for (curr = itr->page_list; curr != NULL; curr = next) {
 		next = curr->next;
-		itr->tree->page_free(curr);
+		rtree_free_page((struct rtree *) itr->tree,
+				(struct rtree_page *) curr);
 	}
 	itr->page_list = NULL;
 	itr->page_pos = RTREE_NEIGHBORS_IN_PAGE;
@@ -648,7 +659,8 @@ rtree_iterator_allocate_neighbour(struct rtree_iterator *itr)
 {
 	if (itr->page_pos >= RTREE_NEIGHBORS_IN_PAGE) {
 		struct rtree_neighbor_page *new_page =
-			(struct rtree_neighbor_page *)itr->tree->page_alloc();
+			(struct rtree_neighbor_page *)
+			rtree_alloc_page((struct rtree*)itr->tree);
 		new_page->next = itr->page_list;
 		itr->page_list = new_page;
 		itr->page_pos = 0;
@@ -683,6 +695,7 @@ rtree_iterator_free_neighbor(struct rtree_iterator *itr,
 void
 rtree_iterator_init(struct rtree_iterator *itr)
 {
+	itr->tree = 0;
 	itr->neigh_list = NULL;
 	itr->neigh_free_list = NULL;
 	itr->page_list = NULL;
@@ -764,22 +777,24 @@ rtree_iterator_next(struct rtree_iterator *itr)
 /*------------------------------------------------------------------------- */
 
 void
-rtree_init(struct rtree *tree,
-	   rtree_page_alloc_t page_alloc, rtree_page_free_t page_free)
+rtree_init(struct rtree *tree, uint32_t extent_size,
+	   rtree_extent_alloc_t extent_alloc, rtree_extent_free_t extent_free)
 {
 	tree->n_records = 0;
 	tree->height = 0;
 	tree->root = NULL;
 	tree->version = 0;
 	tree->n_pages = 0;
-	tree->page_alloc = page_alloc;
-	tree->page_free = page_free;
+	matras_create(&tree->mtab, extent_size, RTREE_PAGE_SIZE,
+		      extent_alloc, extent_free);
+	tree->free_pages = 0;
 }
 
 void
 rtree_destroy(struct rtree *tree)
 {
 	rtree_purge(tree);
+	matras_destroy(&tree->mtab);
 }
 
 void
@@ -859,6 +874,7 @@ rtree_search(const struct rtree *tree, const struct rtree_rect *rect,
 	     enum spatial_search_op op, struct rtree_iterator *itr)
 {
 	rtree_iterator_reset(itr);
+	assert(itr->tree == 0 || itr->tree == tree);
 	itr->tree = tree;
 	itr->version = tree->version;
 	itr->rect = *rect;
diff --git a/src/lib/salad/rtree.h b/src/lib/salad/rtree.h
index f3e6208500..4d1f4c2e28 100644
--- a/src/lib/salad/rtree.h
+++ b/src/lib/salad/rtree.h
@@ -30,6 +30,7 @@
  */
 #include <stddef.h>
 #include <stdbool.h>
+#include "small/matras.h"
 
 /**
  * In-memory Guttman's R-tree
@@ -56,6 +57,7 @@ enum {
 	/**
 	 * R-Tree uses linear search for elements on a page,
 	 * so a larger page size can hurt performance.
+	 * must be power of 2
 	 */
 	RTREE_PAGE_SIZE = 1024
 };
@@ -88,8 +90,8 @@ enum spatial_search_op
 };
 
 /* pointers to page allocation and deallocations functions */
-typedef void *(*rtree_page_alloc_t)();
-typedef void (*rtree_page_free_t)(void *);
+typedef void *(*rtree_extent_alloc_t)();
+typedef void (*rtree_extent_free_t)(void *);
 
 /* A point in RTREE_DIMENSION space */
 struct rtree_point
@@ -124,10 +126,10 @@ struct rtree
 	unsigned version;
 	/* Number of allocated (used) pages */
 	unsigned n_pages;
-	/* Function for allocation new pages */
-	rtree_page_alloc_t page_alloc;
-	/* Function for deallocation new pages */
-	rtree_page_free_t page_free;
+	/* Matras for allocating new page */
+	struct matras mtab;
+	/* List of free pages */
+	void *free_pages;
 };
 
 /* Struct for iteration and retrieving rtree values */
@@ -196,12 +198,13 @@ rtree_set2d(struct rtree_rect *rect,
 /**
  * @brief Initialize a tree
  * @param tree - pointer to a tree
- * @param page_alloc - page allocation function
- * @param page_free - page deallocation function
+ * @param extent_size - size of extents allocated by extent_alloc (see next)
+ * @param extent_alloc - extent allocation function
+ * @param extent_free - extent deallocation function
  */
 void
-rtree_init(struct rtree *tree,
-	   rtree_page_alloc_t page_alloc, rtree_page_free_t page_free);
+rtree_init(struct rtree *tree, uint32_t extent_size,
+	   rtree_extent_alloc_t extent_alloc, rtree_extent_free_t extent_free);
 
 /**
  * @brief Destroy a tree
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 485c94971a..9582135223 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -44,9 +44,9 @@ target_link_libraries(bps_tree.test small)
 add_executable(bps_tree_itr.test bps_tree_itr.cc ${CMAKE_SOURCE_DIR}/third_party/qsort_arg.c)
 target_link_libraries(bps_tree_itr.test small)
 add_executable(rtree.test rtree.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtree.c)
-target_link_libraries(rtree.test)
+target_link_libraries(rtree.test small)
 add_executable(rtree_itr.test rtree_itr.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtree.c)
-target_link_libraries(rtree_itr.test)
+target_link_libraries(rtree_itr.test small)
 add_executable(matras.test matras.cc)
 target_link_libraries(matras.test small)
 add_executable(light.test light.cc)
diff --git a/test/unit/rtree.cc b/test/unit/rtree.cc
index 9e4960c862..e8836deb9d 100644
--- a/test/unit/rtree.cc
+++ b/test/unit/rtree.cc
@@ -9,15 +9,17 @@
 
 static int page_count = 0;
 
+const uint32_t extent_size = RTREE_PAGE_SIZE * 8;
+
 static void *
-page_alloc()
+extent_alloc()
 {
 	page_count++;
-	return malloc(RTREE_PAGE_SIZE);
+	return malloc(extent_size);
 }
 
 static void
-page_free(void *page)
+extent_free(void *page)
 {
 	page_count--;
 	free(page);
@@ -34,7 +36,7 @@ simple_check()
 	header();
 
 	struct rtree tree;
-	rtree_init(&tree, page_alloc, page_free);
+	rtree_init(&tree, extent_size, extent_alloc, extent_free);
 
 	printf("Insert 1..X, remove 1..X\n");
 	for (size_t i = 1; i <= rounds; i++) {
@@ -241,8 +243,6 @@ neighbor_test()
 	header();
 
 	const int test_count = 1000;
-	struct rtree_iterator iterator;
-	rtree_iterator_init(&iterator);
 	struct rtree_rect arr[test_count];
 	static struct rtree_rect basis;
 
@@ -255,10 +255,12 @@ neighbor_test()
 
 	for (size_t i = 0; i <= test_count; i++) {
 		struct rtree tree;
-		rtree_init(&tree, page_alloc, page_free);
+		rtree_init(&tree, extent_size, extent_alloc, extent_free);
 
 		rtree_test_build(&tree, arr, i);
 
+		struct rtree_iterator iterator;
+		rtree_iterator_init(&iterator);
 		if (!rtree_search(&tree, &basis, SOP_NEIGHBOR, &iterator) && i != 0) {
 			fail("search is successful", "true");
 		}
@@ -269,10 +271,10 @@ neighbor_test()
 				fail("wrong search result", "true");
 			}
 		}
+		rtree_iterator_destroy(&iterator);
 		rtree_destroy(&tree);
 	}
 
-	rtree_iterator_destroy(&iterator);
 
 	footer();
 }
diff --git a/test/unit/rtree_itr.cc b/test/unit/rtree_itr.cc
index fae7c43110..08bc959aec 100644
--- a/test/unit/rtree_itr.cc
+++ b/test/unit/rtree_itr.cc
@@ -6,19 +6,21 @@
 #include "unit.h"
 #include "salad/rtree.h"
 
-static int page_count = 0;
+static int extent_count = 0;
+
+const uint32_t extent_size = RTREE_PAGE_SIZE * 8;
 
 static void *
-page_alloc()
+extent_alloc()
 {
-	page_count++;
-	return malloc(RTREE_PAGE_SIZE);
+	extent_count++;
+	return malloc(extent_size);
 }
 
 static void
-page_free(void *page)
+extent_free(void *page)
 {
-	page_count--;
+	extent_count--;
 	free(page);
 }
 
@@ -28,7 +30,7 @@ itr_check()
 	header();
 
 	struct rtree tree;
-	rtree_init(&tree, page_alloc, page_free);
+	rtree_init(&tree, extent_size, extent_alloc, extent_free);
 
 	/* Filling tree */
 	const size_t count1 = 10000;
@@ -182,8 +184,8 @@ itr_check()
 	}
 
 	rtree_purge(&tree);
-	rtree_destroy(&tree);
 	rtree_iterator_destroy(&iterator);
+	rtree_destroy(&tree);
 
 	footer();
 }
@@ -197,9 +199,6 @@ itr_invalidate_check()
 	const size_t max_delete_count = 100;
 	const size_t max_insert_count = 200;
 	const size_t attempt_count = 100;
-	struct rtree_iterator iterators[test_size];
-	for (size_t i = 0; i < test_size; i++)
-		rtree_iterator_init(iterators + i);
 
 	struct rtree_rect rect;
 
@@ -212,7 +211,10 @@ itr_invalidate_check()
 			del_cnt = test_size - del_pos;
 		}
 		struct rtree tree;
-		rtree_init(&tree, page_alloc, page_free);
+		rtree_init(&tree, extent_size, extent_alloc, extent_free);
+		struct rtree_iterator iterators[test_size];
+		for (size_t i = 0; i < test_size; i++)
+			rtree_iterator_init(iterators + i);
 
 		for (size_t i = 0; i < test_size; i++) {
 			rtree_set2d(&rect, i, i, i, i);
@@ -240,6 +242,9 @@ itr_invalidate_check()
 				fail("Iterator was not invalidated (18)", "true");
 			}
 		}
+
+		for (size_t i = 0; i < test_size; i++)
+			rtree_iterator_destroy(iterators + i);
 		rtree_destroy(&tree);
 	}
 
@@ -250,7 +255,10 @@ itr_invalidate_check()
 		size_t ins_cnt = rand() % max_insert_count + 1;
 
 		struct rtree tree;
-		rtree_init(&tree, page_alloc, page_free);
+		rtree_init(&tree, extent_size, extent_alloc, extent_free);
+		struct rtree_iterator iterators[test_size];
+		for (size_t i = 0; i < test_size; i++)
+			rtree_iterator_init(iterators + i);
 
 		for (size_t i = 0; i < test_size; i++) {
 			rtree_set2d(&rect, i, i, i, i);
@@ -276,12 +284,12 @@ itr_invalidate_check()
 				fail("Iterator was not invalidated (22)", "true");
 			}
 		}
+
+		for (size_t i = 0; i < test_size; i++)
+			rtree_iterator_destroy(iterators + i);
 		rtree_destroy(&tree);
 	}
 
-	for (size_t i = 0; i < test_size; i++)
-		rtree_iterator_destroy(iterators + i);
-
 	footer();
 }
 
@@ -290,7 +298,7 @@ main(void)
 {
 	itr_check();
 	itr_invalidate_check();
-	if (page_count != 0) {
+	if (extent_count != 0) {
 		fail("memory leak!", "false");
 	}
 }
-- 
GitLab