From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 299D26EC5D; Fri, 2 Apr 2021 15:36:57 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 299D26EC5D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1617367017; bh=aOSqbYnv8d9sfmnM0kccnt05tpGgsxQsoEEqe/vZjns=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=UdeElmndsMBr0w81YthpOZEYMFXXmvykowuaEN4Onx4f2gXPZuUqDStZDbmVdfUjA 1ozO9RPaxeiFG7XBBtPGOxapbKrHr4eWQlU9+Wj8YYa22MEWwRKZjmJHi2y8vjMbyY zWrPCftfX0hRaM0V3IT9j7kapwkill9OKk+DuBww= Received: from mail-lf1-f51.google.com (mail-lf1-f51.google.com [209.85.167.51]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id A6A6E6EC61 for ; Fri, 2 Apr 2021 15:35:25 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org A6A6E6EC61 Received: by mail-lf1-f51.google.com with SMTP id b14so7372881lfv.8 for ; Fri, 02 Apr 2021 05:35:25 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=sLzHRJiKkYkv2YgOGjzElbrvBdVrWh5F1A5baglIfZM=; b=GqxtS1vMuyIjrhNn2Zb+FVih/JTivFu1UU7TCf8NOuk7ar8wMb/U22GgfXOFewNRN3 LnJadrUkxQQxSIpcsozck0whagnZqe/KQ+mvRs8irzcuXSp5lHfXSiTbgc7xlF+ffzUb BdJU1hHf/XkJ5bUWPpUUYDtv59sZMGMVHy7qcE1IaAI8YyrzudSq0RnptX1iMMfuENVo 9L6mE1w+cHu5lT4GN2F/tgoMHqvWg6Or+f+8apJ2hUi3htKyDPmZbDKWSLDgDvCYwgts h32vlm5pPT2OBu4S1V8s8i31JWto8SKJfMHbCrbfmggI66T502cKPMg0bD2jgEJ05Cpz 76PA== X-Gm-Message-State: AOAM532yahxXR2pYqT/TWgmhPs1tn7MjoM4optqPqJ1z7kYFbbxITXmb GlYqeXuXzfln63A58BQWJtgQdQKbOAGNLg== X-Google-Smtp-Source: ABdhPJyDZaquPyCWjtiIPXrPpKdqJg/Ao7ywQTJpMIgUqPelYY+levQxJ34B0T7gDxhCMdVJqn22cg== X-Received: by 2002:a19:ec1a:: with SMTP id b26mr8296793lfa.610.1617366924306; Fri, 02 Apr 2021 05:35:24 -0700 (PDT) Received: from grain.localdomain ([5.18.171.94]) by smtp.gmail.com with ESMTPSA id j8sm845265lfu.36.2021.04.02.05.35.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 02 Apr 2021 05:35:23 -0700 (PDT) Received: by grain.localdomain (Postfix, from userid 1000) id 2BAF25601F8; Fri, 2 Apr 2021 15:34:22 +0300 (MSK) To: tml Date: Fri, 2 Apr 2021 15:34:18 +0300 Message-Id: <20210402123420.885834-6-gorcunov@gmail.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210402123420.885834-1-gorcunov@gmail.com> References: <20210402123420.885834-1-gorcunov@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH v20 5/7] box/schema.func: switch to new module api X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Cyrill Gorcunov via Tarantool-patches Reply-To: Cyrill Gorcunov Cc: Vladislav Shpilevoy Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" Since we have low level module api now we can switch the box.schema.func code to use it. In particular we define schema_module structure as a wrapper over the modules api -- it carries a pointer to general module structure. Because of modules reload functionality (which was actually a big mistake to introduce in a first place, because it is too fragile and in case of unintended misuse may simply crash the application in a best case) the schema modules carry own cache of modules instances. Thus to make overall code somehow close to modules api we do: 1) every schema_module variable should be named as `mod`. this allows to distinguish this kind of modules from general `module` variables; 2) cache operations are renamed to cache_find/put/update/del; 3) C functions are switched to use module_func low level api; 4) because low level modules api carry own references we can take no explicit reference when calling a function. Part-of #4642 Signed-off-by: Cyrill Gorcunov --- src/box/func.c | 527 +++++++++------------ src/box/func.h | 15 +- src/box/func_def.h | 14 - test/box/gh-4648-func-load-unload.result | 7 +- test/box/gh-4648-func-load-unload.test.lua | 7 +- 5 files changed, 228 insertions(+), 342 deletions(-) diff --git a/src/box/func.c b/src/box/func.c index 08918e6db..54d0cbc68 100644 --- a/src/box/func.c +++ b/src/box/func.c @@ -30,19 +30,13 @@ */ #include "func.h" #include "fiber.h" -#include "trivia/config.h" #include "assoc.h" -#include "lua/utils.h" #include "lua/call.h" -#include "error.h" #include "errinj.h" #include "diag.h" #include "port.h" #include "schema.h" #include "session.h" -#include "libeio/eio.h" -#include -#include /** * Parsed symbol and package names. @@ -56,6 +50,19 @@ struct func_name { const char *package_end; }; +/** + * Schema module (box.schema) instance. + */ +struct schema_module { + /** Low level module instance. */ + struct module *module; + /** List of imported functions. */ + struct rlist funcs; + /** Reference counter. */ + int64_t refs; +}; + + struct func_c { /** Function object base class. */ struct func base; @@ -64,16 +71,21 @@ struct func_c { */ struct rlist item; /** - * For C functions, the body of the function. + * C function to call. */ - box_function_f func; + struct module_func mf; /** - * Each stored function keeps a handle to the - * dynamic library for the C callback. + * A schema module the function belongs to. */ - struct module *module; + struct schema_module *mod; }; +static void +schema_module_ref(struct schema_module *mod); + +static void +schema_module_unref(struct schema_module *mod); + /*** * Split function name to symbol and package names. * For example, str = foo.bar.baz => sym = baz, package = foo.bar @@ -95,82 +107,9 @@ func_split_name(const char *str, struct func_name *name) } } -/** - * Arguments for luaT_module_find used by lua_cpcall() - */ -struct module_find_ctx { - const char *package; - const char *package_end; - char *path; - size_t path_len; -}; - -/** - * A cpcall() helper for module_find() - */ -static int -luaT_module_find(lua_State *L) -{ - struct module_find_ctx *ctx = (struct module_find_ctx *) - lua_topointer(L, 1); - - /* - * Call package.searchpath(name, package.cpath) and use - * the path to the function in dlopen(). - */ - lua_getglobal(L, "package"); - - lua_getfield(L, -1, "search"); - - /* Argument of search: name */ - lua_pushlstring(L, ctx->package, ctx->package_end - ctx->package); - - lua_call(L, 1, 1); - if (lua_isnil(L, -1)) - return luaL_error(L, "module not found"); - /* Convert path to absolute */ - char resolved[PATH_MAX]; - if (realpath(lua_tostring(L, -1), resolved) == NULL) { - diag_set(SystemError, "realpath"); - return luaT_error(L); - } - - snprintf(ctx->path, ctx->path_len, "%s", resolved); - return 0; -} - -/** - * Find path to module using Lua's package.cpath - * @param package package name - * @param package_end a pointer to the last byte in @a package + 1 - * @param[out] path path to shared library - * @param path_len size of @a path buffer - * @retval 0 on success - * @retval -1 on error, diag is set - */ -static int -module_find(const char *package, const char *package_end, char *path, - size_t path_len) -{ - struct module_find_ctx ctx = { package, package_end, path, path_len }; - lua_State *L = tarantool_L; - int top = lua_gettop(L); - if (luaT_cpcall(L, luaT_module_find, &ctx) != 0) { - int package_len = (int) (package_end - package); - diag_set(ClientError, ER_LOAD_MODULE, package_len, package, - lua_tostring(L, -1)); - lua_settop(L, top); - return -1; - } - assert(top == lua_gettop(L)); /* cpcall discard results */ - return 0; -} - +/** Schema modules hash. */ static struct mh_strnptr_t *modules = NULL; -static void -module_gc(struct module *module); - int schema_module_init(void) { @@ -188,10 +127,8 @@ schema_module_free(void) { while (mh_size(modules) > 0) { mh_int_t i = mh_first(modules); - struct module *module = - (struct module *) mh_strnptr_node(modules, i)->val; - /* Can't delete modules if they have active calls */ - module_gc(module); + struct schema_module *mod = mh_strnptr_node(modules, i)->val; + schema_module_unref(mod); } mh_strnptr_delete(modules); } @@ -199,25 +136,30 @@ schema_module_free(void) /** * Look up a module in the modules cache. */ -static struct module * -module_cache_find(const char *name, const char *name_end) +static struct schema_module * +cache_find(const char *name, const char *name_end) { mh_int_t i = mh_strnptr_find_inp(modules, name, name_end - name); if (i == mh_end(modules)) return NULL; - return (struct module *)mh_strnptr_node(modules, i)->val; + return mh_strnptr_node(modules, i)->val; } /** - * Save module to the module cache. + * Save a module to the modules cache. */ -static inline int -module_cache_put(struct module *module) +static int +cache_put(struct schema_module *mod) { - size_t package_len = strlen(module->package); - uint32_t name_hash = mh_strn_hash(module->package, package_len); + const char *str = mod->module->package; + size_t len = mod->module->package_len; + const struct mh_strnptr_node_t strnode = { - module->package, package_len, name_hash, module}; + .str = str, + .len = len, + .hash = mh_strn_hash(str, len), + .val = mod, + }; if (mh_strnptr_put(modules, &strnode, NULL, NULL) == mh_end(modules)) { diag_set(OutOfMemory, sizeof(strnode), "malloc", "modules"); @@ -227,190 +169,214 @@ module_cache_put(struct module *module) } /** - * Delete a module from the module cache + * Update a module in the modules cache. */ static void -module_cache_del(const char *name, const char *name_end) +cache_update(struct schema_module *mod) { - mh_int_t i = mh_strnptr_find_inp(modules, name, name_end - name); + const char *str = mod->module->package; + size_t len = mod->module->package_len; + + mh_int_t i = mh_strnptr_find_inp(modules, str, len); if (i == mh_end(modules)) - return; - mh_strnptr_del(modules, i, NULL); + panic("func: failed to update cache: %s", str); + + mh_strnptr_node(modules, i)->str = str; + mh_strnptr_node(modules, i)->val = mod; } -/* - * Load a dso. - * Create a new symlink based on temporary directory and try to - * load via this symink to load a dso twice for cases of a function - * reload. +/** + * Delete a module from the module cache. */ -static struct module * -module_load(const char *package, const char *package_end) +static void +cache_del(struct schema_module *mod) { - char path[PATH_MAX]; - if (module_find(package, package_end, path, sizeof(path)) != 0) - return NULL; - - int package_len = package_end - package; - struct module *module = (struct module *) - malloc(sizeof(*module) + package_len + 1); - if (module == NULL) { - diag_set(OutOfMemory, sizeof(struct module) + package_len + 1, - "malloc", "struct module"); - return NULL; - } - memcpy(module->package, package, package_len); - module->package[package_len] = 0; - rlist_create(&module->funcs); - module->calls = 0; - - const char *tmpdir = getenv("TMPDIR"); - if (tmpdir == NULL) - tmpdir = "/tmp"; - char dir_name[PATH_MAX]; - int rc = snprintf(dir_name, sizeof(dir_name), "%s/tntXXXXXX", tmpdir); - if (rc < 0 || (size_t) rc >= sizeof(dir_name)) { - diag_set(SystemError, "failed to generate path to tmp dir"); - goto error; + const char *str = mod->module->package; + size_t len = mod->module->package_len; + + mh_int_t i = mh_strnptr_find_inp(modules, str, len); + if (i != mh_end(modules)) { + struct schema_module *v; + v = mh_strnptr_node(modules, i)->val; + /* + * The module may be already reloaded so + * the cache carries a new entry instead. + */ + if (v == mod) + mh_strnptr_del(modules, i, NULL); } +} - if (mkdtemp(dir_name) == NULL) { - diag_set(SystemError, "failed to create unique dir name: %s", - dir_name); - goto error; - } - char load_name[PATH_MAX]; - rc = snprintf(load_name, sizeof(load_name), "%s/%.*s." TARANTOOL_LIBEXT, - dir_name, package_len, package); - if (rc < 0 || (size_t) rc >= sizeof(dir_name)) { - diag_set(SystemError, "failed to generate path to DSO"); - goto error; - } +/** Delete a module. */ +static void +schema_module_delete(struct schema_module *mod) +{ + struct errinj *e = errinj(ERRINJ_DYN_MODULE_COUNT, ERRINJ_INT); + if (e != NULL) + --e->iparam; + module_unload(mod->module); + TRASH(mod); + free(mod); +} - struct stat st; - if (stat(path, &st) < 0) { - diag_set(SystemError, "failed to stat() module %s", path); - goto error; - } +/** Increment reference to a module. */ +static void +schema_module_ref(struct schema_module *mod) +{ + assert(mod->refs >= 0); + ++mod->refs; +} - int source_fd = open(path, O_RDONLY); - if (source_fd < 0) { - diag_set(SystemError, "failed to open module %s file for" \ - " reading", path); - goto error; - } - int dest_fd = open(load_name, O_WRONLY|O_CREAT|O_TRUNC, - st.st_mode & 0777); - if (dest_fd < 0) { - diag_set(SystemError, "failed to open file %s for writing ", - load_name); - close(source_fd); - goto error; +/** Decrement reference to a module and delete it if last one. */ +static void +schema_module_unref(struct schema_module *mod) +{ + assert(mod->refs > 0); + if (--mod->refs == 0) { + cache_del(mod); + schema_module_delete(mod); } +} - off_t ret = eio_sendfile_sync(dest_fd, source_fd, 0, st.st_size); - close(source_fd); - close(dest_fd); - if (ret != st.st_size) { - diag_set(SystemError, "failed to copy DSO %s to %s", - path, load_name); - goto error; +static struct schema_module * +__schema_module_load(const char *name, size_t len, bool force) +{ + struct schema_module *mod = malloc(sizeof(*mod)); + if (mod != NULL) { + mod->module = NULL; + mod->refs = 0; + rlist_create(&mod->funcs); + } else { + diag_set(OutOfMemory, sizeof(*mod), + "malloc", "struct schema_module"); + return NULL; } - module->handle = dlopen(load_name, RTLD_NOW | RTLD_LOCAL); - if (unlink(load_name) != 0) - say_warn("failed to unlink dso link %s", load_name); - if (rmdir(dir_name) != 0) - say_warn("failed to delete temporary dir %s", dir_name); - if (module->handle == NULL) { - diag_set(ClientError, ER_LOAD_MODULE, package_len, - package, dlerror()); - goto error; + if (force) + mod->module = module_load_force(name, len); + else + mod->module = module_load(name, len); + + if (!mod->module) { + free(mod); + return NULL; } + struct errinj *e = errinj(ERRINJ_DYN_MODULE_COUNT, ERRINJ_INT); if (e != NULL) ++e->iparam; - return module; -error: - free(module); - return NULL; + + schema_module_ref(mod); + return mod; } -static void -module_delete(struct module *module) +/** + * Load a new module. + */ +static struct schema_module * +schema_module_load(const char *name, const char *name_end) { - struct errinj *e = errinj(ERRINJ_DYN_MODULE_COUNT, ERRINJ_INT); - if (e != NULL) - --e->iparam; - dlclose(module->handle); - TRASH(module); - free(module); + return __schema_module_load(name, name_end - name, false); } -/* - * Check if a dso is unused and can be closed. +/** + * Force load a new module. */ +static struct schema_module * +schema_module_load_force(const char *name, const char *name_end) +{ + return __schema_module_load(name, name_end - name, true); +} + +static struct func_vtab func_c_vtab; + +/** Create a new C function. */ static void -module_gc(struct module *module) +func_c_create(struct func_c *func_c) { - if (rlist_empty(&module->funcs) && module->calls == 0) - module_delete(module); + func_c->mod = NULL; + func_c->base.vtab = &func_c_vtab; + rlist_create(&func_c->item); + module_func_create(&func_c->mf); } -/* - * Import a function from the module. - */ -static box_function_f -module_sym(struct module *module, const char *name) +static int +schema_func_c_load(struct schema_module *mod, const char *func_name, + struct func_c *func) { - box_function_f f = (box_function_f)dlsym(module->handle, name); - if (f == NULL) { - diag_set(ClientError, ER_LOAD_FUNCTION, name, dlerror()); - return NULL; + assert(module_func_is_empty(&func->mf)); + + if (module_func_load(mod->module, func_name, &func->mf) != 0) + return -1; + + func->mod = mod; + rlist_move(&mod->funcs, &func->item); + schema_module_ref(mod); + + return 0; +} + +static void +schema_func_c_unload(struct func_c *func) +{ + if (!module_func_is_empty(&func->mf)) { + rlist_del(&func->item); + schema_module_unref(func->mod); + module_func_unload(&func->mf); + func_c_create(func); } - return f; } int schema_module_reload(const char *package, const char *package_end) { - struct module *old_module = module_cache_find(package, package_end); - if (old_module == NULL) { + struct schema_module *old = cache_find(package, package_end); + if (old == NULL) { /* Module wasn't loaded - do nothing. */ diag_set(ClientError, ER_NO_SUCH_MODULE, package); return -1; } - struct module *new_module = module_load(package, package_end); - if (new_module == NULL) + struct schema_module *new = schema_module_load_force(package, package_end); + if (new == NULL) return -1; + /* + * Keep an extra reference to the old module + * so it won't be freed until reload is complete, + * otherwise we might free old module then fail + * on some function loading and in result won't + * be able to restore old symbols. + */ + schema_module_ref(old); struct func_c *func, *tmp_func; - rlist_foreach_entry_safe(func, &old_module->funcs, item, tmp_func) { - /* Move immediately for restore sake. */ - rlist_move(&new_module->funcs, &func->item); + rlist_foreach_entry_safe(func, &old->funcs, item, tmp_func) { struct func_name name; func_split_name(func->base.def->name, &name); - func->func = module_sym(new_module, name.sym); - if (func->func == NULL) + schema_func_c_unload(func); + if (schema_func_c_load(new, name.sym, func) != 0) { + /* + * WARN: A hack accessing new->funcs directly + * to start restore from this failing function. + */ + rlist_move(&new->funcs, &func->item); goto restore; - func->module = new_module; + } } - module_cache_del(package, package_end); - if (module_cache_put(new_module) != 0) - goto restore; - module_gc(old_module); + cache_update(new); + schema_module_unref(old); + schema_module_unref(new); return 0; restore: /* * Some old functions are not found int the new module, * thus restore all migrated functions back to original. */ - rlist_foreach_entry_safe(func, &new_module->funcs, item, tmp_func) { + rlist_foreach_entry_safe(func, &new->funcs, item, tmp_func) { struct func_name name; func_split_name(func->base.def->name, &name); - func->func = module_sym(old_module, name.sym); - if (func->func == NULL) { + schema_func_c_unload(func); + if (schema_func_c_load(old, name.sym, func) != 0) { /* * Something strange was happen, an early loaden * function was not found in an old dso. @@ -418,10 +384,9 @@ schema_module_reload(const char *package, const char *package_end) panic("Can't restore module function, " "server state is inconsistent"); } - func->module = old_module; - rlist_move(&old_module->funcs, &func->item); } - module_delete(new_module); + schema_module_unref(old); + schema_module_unref(new); return -1; } @@ -469,8 +434,6 @@ func_new(struct func_def *def) return func; } -static struct func_vtab func_c_vtab; - static struct func * func_c_new(MAYBE_UNUSED struct func_def *def) { @@ -481,35 +444,17 @@ func_c_new(MAYBE_UNUSED struct func_def *def) diag_set(OutOfMemory, sizeof(*func), "malloc", "func"); return NULL; } - func->base.vtab = &func_c_vtab; - func->func = NULL; - func->module = NULL; + func_c_create(func); return &func->base; } -static void -func_c_unload(struct func_c *func) -{ - if (func->module) { - rlist_del(&func->item); - if (rlist_empty(&func->module->funcs)) { - struct func_name name; - func_split_name(func->base.def->name, &name); - module_cache_del(name.package, name.package_end); - } - module_gc(func->module); - } - func->module = NULL; - func->func = NULL; -} - static void func_c_destroy(struct func *base) { assert(base->vtab == &func_c_vtab); assert(base != NULL && base->def->language == FUNC_LANGUAGE_C); struct func_c *func = (struct func_c *) base; - func_c_unload(func); + schema_func_c_unload(func); TRASH(base); free(func); } @@ -521,45 +466,32 @@ func_c_destroy(struct func *base) static int func_c_load(struct func_c *func) { - assert(func->func == NULL); - struct func_name name; func_split_name(func->base.def->name, &name); - struct module *cached, *module; - cached = module_cache_find(name.package, name.package_end); + struct schema_module *cached, *mod; + cached = cache_find(name.package, name.package_end); if (cached == NULL) { - module = module_load(name.package, name.package_end); - if (module == NULL) + mod = schema_module_load(name.package, name.package_end); + if (mod == NULL) return -1; - if (module_cache_put(module)) { - module_delete(module); + if (cache_put(mod)) { + schema_module_unref(mod); return -1; } } else { - module = cached; + mod = cached; + schema_module_ref(mod); } - func->func = module_sym(module, name.sym); - if (func->func == NULL) { - if (cached == NULL) { - /* - * In case if it was a first load we should - * clean the cache immediately otherwise - * the module continue being referenced even - * if there will be no use of it. - * - * Note the module_sym set an error thus be - * careful to not wipe it. - */ - module_cache_del(name.package, name.package_end); - module_delete(module); - } - return -1; - } - func->module = module; - rlist_add(&module->funcs, &func->item); - return 0; + int rc = schema_func_c_load(mod, name.sym, func); + /* + * There is no explicit module loading in this + * interface so each function carries a reference + * by their own. + */ + schema_module_unref(mod); + return rc; } int @@ -568,38 +500,17 @@ func_c_call(struct func *base, struct port *args, struct port *ret) assert(base->vtab == &func_c_vtab); assert(base != NULL && base->def->language == FUNC_LANGUAGE_C); struct func_c *func = (struct func_c *) base; - if (func->func == NULL) { + if (module_func_is_empty(&func->mf)) { if (func_c_load(func) != 0) return -1; } - - struct region *region = &fiber()->gc; - size_t region_svp = region_used(region); - uint32_t data_sz; - const char *data = port_get_msgpack(args, &data_sz); - if (data == NULL) - return -1; - - port_c_create(ret); - box_function_ctx_t ctx = { ret }; - - /* Module can be changed after function reload. */ - struct module *module = func->module; - assert(module != NULL); - ++module->calls; - int rc = func->func(&ctx, data, data + data_sz); - --module->calls; - module_gc(module); - region_truncate(region, region_svp); - if (rc != 0) { - if (diag_last_error(&fiber()->diag) == NULL) { - /* Stored procedure forget to set diag */ - diag_set(ClientError, ER_PROC_C, "unknown error"); - } - port_destroy(ret); - return -1; - } - return rc; + /* + * Note that we don't take a reference to the + * module, it is handled by low level instance, + * thus while been inside the call the associated + * schema_module can be unreferenced and freed. + */ + return module_func_call(&func->mf, args, ret); } static struct func_vtab func_c_vtab = { diff --git a/src/box/func.h b/src/box/func.h index 5a49e34f4..987ca70db 100644 --- a/src/box/func.h +++ b/src/box/func.h @@ -37,6 +37,7 @@ #include "small/rlist.h" #include "func_def.h" #include "user_def.h" +#include "module_cache.h" #if defined(__cplusplus) extern "C" { @@ -44,20 +45,6 @@ extern "C" { struct func; -/** - * Dynamic shared module. - */ -struct module { - /** Module dlhandle. */ - void *handle; - /** List of imported functions. */ - struct rlist funcs; - /** Count of active calls. */ - size_t calls; - /** Module's package name. */ - char package[0]; -}; - /** Virtual method table for func object. */ struct func_vtab { /** Call function with given arguments. */ diff --git a/src/box/func_def.h b/src/box/func_def.h index d99d89190..75cd6a0d3 100644 --- a/src/box/func_def.h +++ b/src/box/func_def.h @@ -168,20 +168,6 @@ func_def_dup(struct func_def *def); int func_def_check(struct func_def *def); -/** - * API of C stored function. - */ - -struct port; - -struct box_function_ctx { - struct port *port; -}; - -typedef struct box_function_ctx box_function_ctx_t; -typedef int (*box_function_f)(box_function_ctx_t *ctx, - const char *args, const char *args_end); - #ifdef __cplusplus } #endif diff --git a/test/box/gh-4648-func-load-unload.result b/test/box/gh-4648-func-load-unload.result index e695dd365..91b78d083 100644 --- a/test/box/gh-4648-func-load-unload.result +++ b/test/box/gh-4648-func-load-unload.result @@ -71,7 +71,8 @@ check_module_count_diff(-1) | ... -- A not finished invocation of a function from a module prevents --- its unload. Until the call is finished. +-- low level module intance unload while schema level module is +-- free to unload immediately when dropped. box.schema.func.create('function1', {language = 'C'}) | --- | ... @@ -110,7 +111,7 @@ box.schema.func.drop('function1') box.schema.func.drop('function1.test_sleep') | --- | ... -check_module_count_diff(0) +check_module_count_diff(-1) | --- | ... @@ -132,6 +133,6 @@ test_run:wait_cond(function() return f2:status() == 'dead' end) | --- | - true | ... -check_module_count_diff(-1) +check_module_count_diff(0) | --- | ... diff --git a/test/box/gh-4648-func-load-unload.test.lua b/test/box/gh-4648-func-load-unload.test.lua index 10b9de87a..4c4f30af7 100644 --- a/test/box/gh-4648-func-load-unload.test.lua +++ b/test/box/gh-4648-func-load-unload.test.lua @@ -36,7 +36,8 @@ box.schema.func.drop('function1') check_module_count_diff(-1) -- A not finished invocation of a function from a module prevents --- its unload. Until the call is finished. +-- low level module intance unload while schema level module is +-- free to unload immediately when dropped. box.schema.func.create('function1', {language = 'C'}) box.schema.func.create('function1.test_sleep', {language = 'C'}) check_module_count_diff(0) @@ -52,7 +53,7 @@ box.func.function1:call() check_module_count_diff(1) box.schema.func.drop('function1') box.schema.func.drop('function1.test_sleep') -check_module_count_diff(0) +check_module_count_diff(-1) f1:cancel() test_run:wait_cond(function() return f1:status() == 'dead' end) @@ -60,4 +61,4 @@ check_module_count_diff(0) f2:cancel() test_run:wait_cond(function() return f2:status() == 'dead' end) -check_module_count_diff(-1) +check_module_count_diff(0) -- 2.30.2