From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Kirill Shcherbatov Subject: [PATCH v2 1/5] box: introduce tuple_extra infrastructure Date: Mon, 24 Jun 2019 17:27:00 +0300 Message-Id: <26c1864bda91499b7a3615ea3aa583dab1a57359.1561384554.git.kshcherbatov@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: tarantool-patches@freelists.org, vdavydov.dev@gmail.com Cc: Kirill Shcherbatov List-ID: Introduced a new object tuple_extra: a memory allocation is associated with given tuple and chunk_id. Extended tuple_format's vtab with few new methods to manage tuple_extras lifecycle. Supported tuple_extra for memtx engine: a memory chunks are allocated with memtx's smalloc allocator and are registered in tuple_exta_cache: an engine-independent hashtable -> . Needed for #1260 --- src/box/memtx_engine.c | 43 +++++++++++++++++++++++++ src/box/tuple.c | 65 ++++++++++++++++++++++++++++++++++++++ src/box/tuple.h | 71 ++++++++++++++++++++++++++++++++++++++++++ src/box/tuple_format.h | 22 +++++++++++++ src/box/vy_stmt.c | 3 ++ 5 files changed, 204 insertions(+) diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c index cd763e547..294f4d73d 100644 --- a/src/box/memtx_engine.c +++ b/src/box/memtx_engine.c @@ -1199,9 +1199,52 @@ memtx_tuple_delete(struct tuple_format *format, struct tuple *tuple) smfree_delayed(&memtx->alloc, memtx_tuple, total); } +void +metmx_tuple_extra_delete(struct tuple_format *format, + struct tuple_extra *tuple_extra) +{ + struct memtx_engine *memtx = (struct memtx_engine *)format->engine; + tuple_extra_cache_unregister(tuple_extra); + uint32_t sz = tuple_extra_sz(tuple_extra->data_sz); + smfree(&memtx->alloc, tuple_extra, sz); +} + +struct tuple_extra * +memtx_tuple_extra_new(struct tuple_format *format, struct tuple *tuple, + uint32_t chunk_id, uint32_t data_sz) +{ + struct memtx_engine *memtx = (struct memtx_engine *)format->engine; + uint32_t sz = tuple_extra_sz(data_sz); + struct tuple_extra *tuple_extra = + (struct tuple_extra *) smalloc(&memtx->alloc, sz); + if (tuple == NULL) { + diag_set(OutOfMemory, sz, "smalloc", "tuple"); + return NULL; + } + tuple_extra->tuple = tuple; + tuple_extra->chunk_id = chunk_id; + tuple_extra->data_sz = data_sz; + if (tuple_extra_cache_register(tuple_extra) != 0) { + smfree(&memtx->alloc, tuple_extra, sz); + return NULL; + } + return tuple_extra; +} + +static inline struct tuple_extra * +memtx_tuple_extra_get(struct tuple_format *format, struct tuple *tuple, + uint32_t chunk_id) +{ + (void) format; + return tuple_extra_cache_find(tuple, chunk_id); +} + struct tuple_format_vtab memtx_tuple_format_vtab = { memtx_tuple_delete, memtx_tuple_new, + metmx_tuple_extra_delete, + memtx_tuple_extra_new, + memtx_tuple_extra_get, }; /** diff --git a/src/box/tuple.c b/src/box/tuple.c index a7ef332b2..9512543d6 100644 --- a/src/box/tuple.c +++ b/src/box/tuple.c @@ -42,6 +42,21 @@ static struct mempool tuple_iterator_pool; static struct small_alloc runtime_alloc; +/** Map: (struct tuple *) => (struct tuple_extra *). */ +#define MH_SOURCE 1 +#define mh_name _tuple_extra +#define mh_key_t struct tuple * +#define mh_node_t struct tuple_extra * +#define mh_arg_t uint32_t +#define mh_hash(a, arg) ((uintptr_t)(*(a))->tuple) +#define mh_hash_key(a, arg) ((uintptr_t) a) +#define mh_cmp(a, b, arg) ((*(a))->tuple != (*(b))->tuple || \ + (*(a))->chunk_id != (*(b))->chunk_id) +#define mh_cmp_key(a, b, arg) ((a) != (*(b))->tuple || (arg) != (*(b))->chunk_id) +#include "salad/mhash.h" + +static struct mh_tuple_extra_t *tuple_extra_cache; + enum { /** Lowest allowed slab_alloc_minimal */ OBJSIZE_MIN = 16, @@ -67,6 +82,9 @@ runtime_tuple_new(struct tuple_format *format, const char *data, const char *end static struct tuple_format_vtab tuple_format_runtime_vtab = { runtime_tuple_delete, runtime_tuple_new, + NULL, + NULL, + NULL, }; static struct tuple * @@ -301,6 +319,10 @@ tuple_init(field_name_hash_f hash) if (tuple_format_runtime == NULL) return -1; + tuple_extra_cache = mh_tuple_extra_new(); + if (tuple_extra_cache == NULL) + return -1; + /* Make sure this one stays around. */ tuple_format_ref(tuple_format_runtime); @@ -374,6 +396,9 @@ tuple_free(void) mempool_destroy(&tuple_iterator_pool); small_alloc_destroy(&runtime_alloc); + assert(mh_size(tuple_extra_cache) == 0); + mh_tuple_extra_delete(tuple_extra_cache); + tuple_format_free(); coll_id_cache_destroy(); @@ -785,3 +810,43 @@ mp_str(const char *data) return ""; return buf; } + +uint32_t +tuple_extra_sz(uint32_t data_sz) +{ + return sizeof(struct tuple_extra) + data_sz; +} + +int +tuple_extra_cache_register(struct tuple_extra *tuple_extra) +{ + mh_int_t id = mh_tuple_extra_put(tuple_extra_cache, + (const struct tuple_extra **)&tuple_extra, + NULL, 0); + if (id == mh_end(tuple_extra_cache)) + return -1; + return 0; +} + +void +tuple_extra_cache_unregister(struct tuple_extra *tuple_extra) +{ + struct tuple *tuple = tuple_extra->tuple; + uint32_t chunk_id = tuple_extra->chunk_id; + mh_int_t id = mh_tuple_extra_find(tuple_extra_cache, + tuple, chunk_id); + assert(id != mh_end(tuple_extra_cache)); + mh_tuple_extra_del(tuple_extra_cache, id, chunk_id); +} + +struct tuple_extra * +tuple_extra_cache_find(struct tuple *tuple, uint32_t chunk_id) +{ + mh_int_t id = mh_tuple_extra_find(tuple_extra_cache, tuple, chunk_id); + if (id == mh_end(tuple_extra_cache)) + return NULL; + struct tuple_extra **tuple_extra_ptr = + mh_tuple_extra_node(tuple_extra_cache, id); + assert(tuple_extra_ptr != NULL && *tuple_extra_ptr != NULL); + return *tuple_extra_ptr; +} diff --git a/src/box/tuple.h b/src/box/tuple.h index 99dfeb82d..3504c1d95 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -447,6 +447,77 @@ tuple_delete(struct tuple *tuple) format->vtab.tuple_delete(format, tuple); } +/** Tuple metadata hashtable entry. */ +struct tuple_extra { + /** Tuple pointer is used as a hash key. */ + struct tuple *tuple; + /** An extention identifier. */ + uint32_t chunk_id; + /** The payload size. Needed to perform memory release.*/ + uint32_t data_sz; + /** Metadata object payload. */ + char data[0]; +}; + +/** Calculate the size of tuple_extra object by given data_sz. */ +uint32_t +tuple_extra_sz(uint32_t data_sz); + +/** + * Lookup for tuple_extra by given tuple and chunk_id in the tuple + * extra hashtable. + */ +struct tuple_extra * +tuple_extra_cache_find(struct tuple *tuple, uint32_t chunk_id); + +/** + * Register a new tuple_extra extention in the tuple extra + * hashtable. + */ +int +tuple_extra_cache_register(struct tuple_extra *tuple_extra); + +/** + * Unregister a given tuple_extra extention in the tuple extra + * hashtable. + */ +void +tuple_extra_cache_unregister(struct tuple_extra *tuple_extra); + +/** + * Allocate a new extra allocation for given tuple and + * unique chunk_id identifier. + */ +static inline struct tuple_extra * +tuple_extra_new(struct tuple *tuple, uint32_t chunk_id, uint32_t data_sz) +{ + struct tuple_format *format = tuple_format(tuple); + return format->vtab.tuple_extra_new(format, tuple, chunk_id, data_sz); +} + +/** + * Free allocated tuple extra for given tuple and unique + * chunk_id identifier. + */ +static inline void +tuple_extra_delete(struct tuple_extra *tuple_extra) +{ + struct tuple_format *format = + tuple_format(tuple_extra->tuple); + format->vtab.tuple_extra_delete(format, tuple_extra); +} + +/** + * Get an existent extra allocation for given tuple and + * unique chunk_id identifier. + */ +static inline struct tuple_extra * +tuple_extra_get(struct tuple *tuple, uint32_t chunk_id) +{ + struct tuple_format *format = tuple_format(tuple); + return format->vtab.tuple_extra_get(format, tuple, chunk_id); +} + /** * Check tuple data correspondence to space format. * Actually, checks everything that is checked by diff --git a/src/box/tuple_format.h b/src/box/tuple_format.h index e4f5f0018..8b37a48d7 100644 --- a/src/box/tuple_format.h +++ b/src/box/tuple_format.h @@ -64,6 +64,7 @@ enum { TUPLE_INDEX_BASE = 1 }; enum { TUPLE_OFFSET_SLOT_NIL = INT32_MAX }; struct tuple; +struct tuple_extra; struct tuple_format; struct coll; @@ -82,6 +83,27 @@ struct tuple_format_vtab { struct tuple* (*tuple_new)(struct tuple_format *format, const char *data, const char *end); + /** + * Free allocated tuple extra for given tuple and unique + * chunk_id identifier. + */ + void + (*tuple_extra_delete)(struct tuple_format *format, + struct tuple_extra *tuple_extra); + /** + * Allocate a new extra allocation for given tuple and + * unique chunk_id identifier. + */ + struct tuple_extra * + (*tuple_extra_new)(struct tuple_format *format, struct tuple *tuple, + uint32_t chunk_id, uint32_t data_sz); + /** + * Get an existent extra allocation for given tuple and + * unique chunk_id identifier. + */ + struct tuple_extra * + (*tuple_extra_get)(struct tuple_format *format, struct tuple *tuple, + uint32_t chunk_id); }; /** Tuple field meta information for tuple_format. */ diff --git a/src/box/vy_stmt.c b/src/box/vy_stmt.c index f936cd61f..5c79eac41 100644 --- a/src/box/vy_stmt.c +++ b/src/box/vy_stmt.c @@ -118,6 +118,9 @@ vy_stmt_env_create(struct vy_stmt_env *env) { env->tuple_format_vtab.tuple_new = vy_tuple_new; env->tuple_format_vtab.tuple_delete = vy_tuple_delete; + env->tuple_format_vtab.tuple_extra_new = NULL; + env->tuple_format_vtab.tuple_extra_delete = NULL; + env->tuple_format_vtab.tuple_extra_get = NULL; env->max_tuple_size = 1024 * 1024; env->key_format = vy_stmt_format_new(env, NULL, 0, NULL, 0, 0, NULL); if (env->key_format == NULL) -- 2.21.0