From bc88b32554bd93e5b144726c036b26a6d2348b0f Mon Sep 17 00:00:00 2001
From: mechanik20051988 <mechanik20.05.1988@gmail.com>
Date: Tue, 23 Mar 2021 13:36:58 +0300
Subject: [PATCH] memtx: implement system allocator, based on malloc

Slab allocator, which is used for tuples allocation,
has a certain disadvantage - it tends to unresolvable
fragmentation on certain workloads (size migration).
In this case user should be able to choose other allocator.
System allocator based on malloc function, but restricted
by the same qouta as slab allocator. System allocator
does not alloc all memory at start, istead, it allocate
memory as needed, checking that quota is not exceeded.
Part of #5419
---
 src/box/CMakeLists.txt |   1 +
 src/box/allocator.h    |  35 +++++++++++++
 src/box/sysalloc.c     | 113 +++++++++++++++++++++++++++++++++++++++++
 src/box/sysalloc.h     |  83 ++++++++++++++++++++++++++++++
 4 files changed, 232 insertions(+)
 create mode 100644 src/box/sysalloc.c
 create mode 100644 src/box/sysalloc.h

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 371535677b..012670818a 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -132,6 +132,7 @@ add_library(box STATIC
     memtx_engine.cc
     memtx_space.c
     sysview.c
+    sysalloc.c
     blackhole.c
     service_engine.c
     session_settings.c
diff --git a/src/box/allocator.h b/src/box/allocator.h
index 9b8df6bc15..dd747f7620 100644
--- a/src/box/allocator.h
+++ b/src/box/allocator.h
@@ -30,6 +30,7 @@
  * SUCH DAMAGE.
  */
 #include <small/small.h>
+#include "sysalloc.h"
 
 struct alloc_stats {
 	size_t used;
@@ -88,3 +89,37 @@ class SmallAlloc
 private:
 	static struct small_alloc small_alloc;
 };
+
+class SysAlloc
+{
+public:
+	static inline void
+	create(struct quota *quota)
+	{
+		sys_alloc_create(&sys_alloc, quota);
+	}
+	static inline void
+	destroy(void)
+	{
+		sys_alloc_destroy(&sys_alloc);
+	}
+	static inline void *
+	alloc(size_t size)
+	{
+		return sysalloc(&sys_alloc, size);
+	};
+	static inline void
+	free(void *ptr, size_t size)
+	{
+		return sysfree(&sys_alloc, ptr, size);
+	}
+	static inline void
+	stats(struct alloc_stats *alloc_stats)
+	{
+		struct sys_stats data_stats;
+		sys_stats(&sys_alloc, &data_stats);
+		alloc_stats->used = data_stats.used;
+	}
+private:
+	static struct sys_alloc sys_alloc;
+};
diff --git a/src/box/sysalloc.c b/src/box/sysalloc.c
new file mode 100644
index 0000000000..496aadd101
--- /dev/null
+++ b/src/box/sysalloc.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "sysalloc.h"
+
+#include <small/quota.h>
+
+struct container {
+	struct rlist rlist;
+	size_t bytes;
+};
+
+void
+sys_alloc_create(struct sys_alloc *alloc, struct quota *quota)
+{
+	alloc->used_bytes = 0;
+	alloc->quota = quota;
+	rlist_create(&alloc->allocations);
+#ifndef _NDEBUG
+	alloc->thread_id = pthread_self();
+#endif
+}
+
+void
+sys_alloc_destroy(struct sys_alloc *alloc)
+{
+	assert(pthread_equal(alloc->thread_id, pthread_self()));
+	struct container *item, *tmp;
+	rlist_foreach_entry_safe(item, &alloc->allocations, rlist, tmp)
+		sysfree(alloc, ((void *)item) + sizeof(struct container), item->bytes);
+	assert(alloc->used_bytes == 0);
+}
+
+void *
+sysalloc(struct sys_alloc *alloc, size_t bytes)
+{
+	assert(pthread_equal(alloc->thread_id, pthread_self()));
+	void *ptr = malloc(sizeof(struct container) + bytes);
+	if (ptr == NULL)
+		return NULL;
+	/*
+	 * The limit on the amount of memory available to allocator
+	 * is stored in struct quota. We request from the quota at least
+	 * QUOTA_UNIT_SIZE bytes. Divide the requested bytes into two parts:
+	 * integer number of blocks by QUOTA_UNIT_SIZE bytes and remaining
+	 * bytes count.
+	 */
+	size_t remaining = bytes % QUOTA_UNIT_SIZE;
+	size_t units = bytes / QUOTA_UNIT_SIZE;
+	/*
+	 * Check that we need one more quota block for remaining bytes
+	 */
+	if (small_align(alloc->used_bytes, QUOTA_UNIT_SIZE) <
+	    small_align(alloc->used_bytes + remaining, QUOTA_UNIT_SIZE))
+			units++;
+	if (units > 0 &&
+	    quota_use(alloc->quota, units * QUOTA_UNIT_SIZE) < 0) {
+		free(ptr);
+		return NULL;
+	}
+	alloc->used_bytes += bytes;
+	((struct container *)ptr)->bytes = bytes;
+	rlist_add_entry(&alloc->allocations, (struct container *)ptr, rlist);
+	return ptr + sizeof(struct container);
+}
+
+void
+sysfree(struct sys_alloc *alloc, void *ptr, size_t bytes)
+{
+	assert(alloc->thread_id == pthread_self());
+	ptr -= sizeof(struct container);
+	size_t remaining = bytes % QUOTA_UNIT_SIZE;
+	size_t units = bytes / QUOTA_UNIT_SIZE;
+	/*
+	 * Check that we can free one more quota block when we
+	 * released remaining bytes
+	 */
+	if (small_align(alloc->used_bytes, QUOTA_UNIT_SIZE) >
+	    small_align(alloc->used_bytes - remaining, QUOTA_UNIT_SIZE))
+		units++;
+	alloc->used_bytes -= bytes;
+	if (units > 0)
+		quota_release(alloc->quota, units * QUOTA_UNIT_SIZE);
+	rlist_del_entry((struct container *)ptr, rlist);
+	free(ptr);
+}
diff --git a/src/box/sysalloc.h b/src/box/sysalloc.h
new file mode 100644
index 0000000000..38f43cb353
--- /dev/null
+++ b/src/box/sysalloc.h
@@ -0,0 +1,83 @@
+#pragma once
+/*
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <small/small.h>
+
+#include <pthread.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct sys_stats {
+	size_t used;
+};
+
+struct sys_alloc {
+	/** Allocated bytes */
+	uint64_t used_bytes;
+	/** The source of allocations */
+	struct quota *quota;
+	/**
+	 * List of allocations, used to free up
+	 * memory, when allocator is destroyed
+	 */
+	struct rlist allocations;
+#ifndef _NDEBUG
+	/**
+	 * Debug only: track that all allocations
+	 * are made from a single thread.
+	 */
+	pthread_t thread_id;
+#endif
+};
+
+static inline void
+sys_stats(struct sys_alloc *alloc, struct sys_stats *totals)
+{
+	totals->used = alloc->used_bytes;
+}
+
+void
+sys_alloc_create(struct sys_alloc *alloc, struct quota *quota);
+
+void
+sys_alloc_destroy(struct sys_alloc *alloc);
+
+void *
+sysalloc(struct sys_alloc *alloc, size_t size);
+
+void
+sysfree(struct sys_alloc *alloc, void *ptr, size_t size);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
-- 
GitLab