Tarantool development patches archive
 help / color / mirror / Atom feed
From: Alexander Turenko <alexander.turenko@tarantool.org>
To: Vladislav Shpilevoy <v.shpilevoy@tarantool.org>
Cc: tarantool-patches@dev.tarantool.org,
	Alexander Turenko <alexander.turenko@tarantool.org>
Subject: [Tarantool-patches] [PATCH v2 02/15] module api: expose box region
Date: Sun, 11 Oct 2020 15:57:35 +0300	[thread overview]
Message-ID: <e93f5c255ba14886a53071740c94591b2be7f241.1602420460.git.alexander.turenko@tarantool.org> (raw)
In-Reply-To: <cover.1602420460.git.alexander.turenko@tarantool.org>

It is the better alternative to linking the small library directly to a
module. Why not just use the small library in a module?

Functions from an executable are preferred over ones that are shipped in
a dynamic library (on Linux, Mac OS differs), while a module may use the
small library directly. It may cause a problem when some functions from
the library are inlined, but some are not, and those different versions
of the library offer structures with different layouts. Small library
symbols may be exported by the tarantool executable after the change of
default symbols visibility (see [1]). See more details and examples in
[2].

So it is better to expose so called box region and get rid of all those
compatibility problems.

[1]: 2.5.0-42-g03790ac55 ('cmake: remove dynamic-list linker option')
[2]: https://lists.tarantool.org/pipermail/tarantool-discussions/2020-September/000095.html

Part of #5273
---
 src/exports.h                    |  4 ++
 src/lib/core/fiber.c             | 24 +++++++++
 src/lib/core/fiber.h             | 76 +++++++++++++++++++++++++++++
 test/app-tap/module_api.c        | 84 ++++++++++++++++++++++++++++++++
 test/app-tap/module_api.test.lua |  2 +-
 5 files changed, 189 insertions(+), 1 deletion(-)

diff --git a/src/exports.h b/src/exports.h
index 6d8303180..7861bb529 100644
--- a/src/exports.h
+++ b/src/exports.h
@@ -218,6 +218,10 @@ EXPORT(fiber_is_cancelled)
 EXPORT(fiber_join)
 EXPORT(fiber_new)
 EXPORT(fiber_new_ex)
+EXPORT(box_region_aligned_alloc)
+EXPORT(box_region_alloc)
+EXPORT(box_region_truncate)
+EXPORT(box_region_used)
 EXPORT(fiber_reschedule)
 EXPORT(fiber_self)
 EXPORT(fiber_set_cancellable)
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 223c841df..33ec47c66 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -1368,6 +1368,30 @@ fiber_top_disable(void)
 }
 #endif /* ENABLE_FIBER_TOP */
 
+size_t
+box_region_used(void)
+{
+	return region_used(&fiber()->gc);
+}
+
+void *
+box_region_alloc(size_t size)
+{
+	return region_alloc(&fiber()->gc, size);
+}
+
+void *
+box_region_aligned_alloc(size_t size, size_t alignment)
+{
+	return region_aligned_alloc(&fiber()->gc, size, alignment);
+}
+
+void
+box_region_truncate(size_t size)
+{
+	return region_truncate(&fiber()->gc, size);
+}
+
 void
 cord_create(struct cord *cord, const char *name)
 {
diff --git a/src/lib/core/fiber.h b/src/lib/core/fiber.h
index 16ee9f414..11ca278e5 100644
--- a/src/lib/core/fiber.h
+++ b/src/lib/core/fiber.h
@@ -386,6 +386,82 @@ struct slab_cache;
 API_EXPORT struct slab_cache *
 cord_slab_cache(void);
 
+/**
+ * box region allocator
+ *
+ * It is the region allocator from the small library. It is useful
+ * for allocating tons of small objects and free them at once.
+ *
+ * Typical usage is illustrated in the sketch below.
+ *
+ *  | size_t region_svp = box_region_used();
+ *  | while (<...>) {
+ *  |     char *buf = box_region_alloc(<...>);
+ *  |     <...>
+ *  | }
+ *  | box_region_truncate(region_svp);
+ *
+ * There are module API functions that return a result on
+ * this region. In this case a module is responsible to free the
+ * result:
+ *
+ *  | size_t region_svp = box_region_used();
+ *  | char *buf = box_<...>(<...>);
+ *  | <...>
+ *  | box_region_truncate(region_svp);
+ *
+ * This API provides better compatibility guarantees over using
+ * the small library directly in a module. A binary layout of
+ * internal structures may be changed in a future, but
+ * <box_region_*>() functions will remain API and ABI compatible.
+ *
+ * Data allocated on the region are guaranteed to be valid until
+ * a fiber yield or a call of a function from the certain set:
+ *
+ * - Related to transactions.
+ * - Ones that may cause box initialization (box.cfg()).
+ * - Ones that may involve SQL execution.
+ *
+ * FIXME: Provide more strict list of functions, which may
+ * invalidate the data: ones that may lead to calling of
+ * fiber_gc().
+ *
+ * It is safe to call simple box APIs around tuples, key_defs and
+ * so on -- they don't invalidate the allocated data.
+ *
+ * Don't assume this region as fiber local. This is an
+ * implementation detail and may be changed in a future.
+ */
+
+/** How much memory is used by the box region. */
+API_EXPORT size_t
+box_region_used(void);
+
+/**
+ * Allocate size bytes from the box region.
+ *
+ * Don't use this function to allocate a memory block for a value
+ * or array of values of a type with alignment requirements. A
+ * violation of alignment requrements leads to undefined
+ * behaviour.
+ */
+API_EXPORT void *
+box_region_alloc(size_t size);
+
+/**
+ * Allocate size bytes from the box region with given alignment.
+ *
+ * Alignment must be a power of 2.
+ */
+API_EXPORT void *
+box_region_aligned_alloc(size_t size, size_t alignment);
+
+/**
+ * Truncate the box region to the given size.
+ */
+void
+box_region_truncate(size_t size);
+
 /** \endcond public */
 
 /**
diff --git a/test/app-tap/module_api.c b/test/app-tap/module_api.c
index a79fbed0d..b5949cde4 100644
--- a/test/app-tap/module_api.c
+++ b/test/app-tap/module_api.c
@@ -15,6 +15,10 @@
 #define STR2(x) #x
 #define STR(x) STR2(x)
 
+#ifndef lengthof
+#define lengthof(array) (sizeof (array) / sizeof ((array)[0]))
+#endif
+
 /* Test for constants */
 static const char *consts[] = {
 	PACKAGE_VERSION,
@@ -451,6 +455,85 @@ test_iscallable(lua_State *L)
 	return 1;
 }
 
+/* {{{ test_box_region */
+
+/**
+ * Verify basic usage of box region.
+ */
+static int
+test_box_region(struct lua_State *L)
+{
+	size_t region_svp_0 = box_region_used();
+
+	/* Verify allocation and box_region_used(). */
+	size_t size_arr[] = {1, 7, 19, 10 * 1024 * 1024, 1, 18, 1024};
+	size_t region_svp_arr[lengthof(size_arr)];
+	char *ptr_arr[lengthof(size_arr)];
+	for (size_t i = 0; i < lengthof(size_arr); ++i) {
+		size_t size = size_arr[i];
+		size_t region_svp = box_region_used();
+		char *ptr = box_region_alloc(size);
+
+		/* Verify box_region_used() after allocation. */
+		assert(box_region_used() - region_svp == size);
+
+		/* Verify that data is accessible. */
+		for (char *p = ptr; p < ptr + size; ++p)
+			*p = 'x';
+
+		/*
+		 * Save data pointer and savepoint to verify
+		 * truncation later.
+		 */
+		ptr_arr[i] = ptr;
+		region_svp_arr[i] = region_svp;
+	}
+
+	/* Verify truncation. */
+	for (ssize_t i = lengthof(region_svp_arr) - 1; i >= 0; --i) {
+		box_region_truncate(region_svp_arr[i]);
+		assert(box_region_used() == region_svp_arr[i]);
+
+		/*
+		 * Verify that all data before this savepoint
+		 * still accessible.
+		 */
+		for (ssize_t j = 0; j < i; ++j) {
+			size_t size = size_arr[j];
+			char *ptr = ptr_arr[j];
+			for (char *p = ptr; p < ptr + size; ++p) {
+				assert(*p == 'x' || *p == 'y');
+				*p = 'y';
+			}
+		}
+	}
+	assert(box_region_used() == region_svp_0);
+
+	/* Verify aligned allocation. */
+	size_t a_size_arr[] = {1, 3, 5, 7, 11, 13, 17, 19};
+	size_t alignment_arr[] = {1, 2, 4, 8, 16, 32, 64};
+	for (size_t s = 0; s < lengthof(a_size_arr); ++s) {
+		for (size_t a = 0; a < lengthof(alignment_arr); ++a) {
+			size_t size = a_size_arr[s];
+			size_t alignment = alignment_arr[a];
+			char *ptr = box_region_aligned_alloc(size, alignment);
+			assert((uintptr_t) ptr % alignment == 0);
+
+			/* Data is accessible. */
+			for (char *p = ptr; p < ptr + size; ++p)
+				*p = 'x';
+		}
+	}
+
+	/* Clean up. */
+	box_region_truncate(region_svp_0);
+
+	lua_pushboolean(L, true);
+	return 1;
+}
+
+/* }}} test_box_region */
+
 LUA_API int
 luaopen_module_api(lua_State *L)
 {
@@ -479,6 +562,7 @@ luaopen_module_api(lua_State *L)
 		{"test_state", test_state},
 		{"test_tostring", test_tostring},
 		{"iscallable", test_iscallable},
+		{"test_box_region", test_box_region},
 		{NULL, NULL}
 	};
 	luaL_register(L, "module_api", lib);
diff --git a/test/app-tap/module_api.test.lua b/test/app-tap/module_api.test.lua
index a6658cc61..08e8add35 100755
--- a/test/app-tap/module_api.test.lua
+++ b/test/app-tap/module_api.test.lua
@@ -117,7 +117,7 @@ local function test_iscallable(test, module)
 end
 
 local test = require('tap').test("module_api", function(test)
-    test:plan(24)
+    test:plan(25)
     local status, module = pcall(require, 'module_api')
     test:is(status, true, "module")
     test:ok(status, "module is loaded")
-- 
2.25.0

  parent reply	other threads:[~2020-10-11 12:57 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-11 12:57 [Tarantool-patches] [PATCH v2 00/15] RFC: module api: extend for external key_def Lua module Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 01/15] module api: get rid of typedef redefinitions Alexander Turenko
2020-10-11 12:57 ` Alexander Turenko [this message]
2020-10-11 15:26   ` [Tarantool-patches] [PATCH v2 02/15] module api: expose box region Vladislav Shpilevoy
2020-10-12  6:07     ` Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 03/15] module api/lua: add luaL_iscdata() function Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 04/15] lua: factor out tuple encoding from luaT_tuple_new Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 05/15] lua: don't raise a Lua error from luaT_tuple_new() Alexander Turenko
2020-10-11 15:25   ` Vladislav Shpilevoy
2020-10-12 10:37     ` Alexander Turenko
2020-10-12 13:34       ` Timur Safin
2020-10-14 23:41       ` Vladislav Shpilevoy
2020-10-15 19:43         ` Alexander Turenko
2020-10-15 22:10           ` Vladislav Shpilevoy
2020-10-11 17:47   ` Igor Munkin
2020-10-11 18:08     ` Igor Munkin
2020-10-12 10:37     ` Alexander Turenko
2020-10-12 10:51       ` Igor Munkin
2020-10-12 18:41         ` Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 06/15] WIP: module api/lua: add luaT_tuple_encode() Alexander Turenko
2020-10-11 15:25   ` Vladislav Shpilevoy
2020-10-12 10:35     ` Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 07/15] module api/lua: expose luaT_tuple_new() Alexander Turenko
2020-10-11 15:25   ` Vladislav Shpilevoy
2020-10-12  6:11     ` Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 08/15] module api/lua: add API_EXPORT to tuple functions Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 09/15] module api: add API_EXPORT to key_def functions Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 10/15] module api: add box_key_def_new_v2() Alexander Turenko
2020-10-11 15:25   ` Vladislav Shpilevoy
2020-10-12  7:21     ` Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 11/15] module api: add box_key_def_dump_parts() Alexander Turenko
2020-10-11 15:25   ` Vladislav Shpilevoy
2020-10-12  6:50     ` Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 12/15] module api: expose box_key_def_validate_tuple() Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 13/15] WIP: module api: expose box_key_def_merge() Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 14/15] WIP: module api: expose box_key_def_extract_key() Alexander Turenko
2020-10-11 12:57 ` [Tarantool-patches] [PATCH v2 15/15] WIP: module api: add box_key_def_validate_key() Alexander Turenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e93f5c255ba14886a53071740c94591b2be7f241.1602420460.git.alexander.turenko@tarantool.org \
    --to=alexander.turenko@tarantool.org \
    --cc=tarantool-patches@dev.tarantool.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH v2 02/15] module api: expose box region' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox