From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> To: Igor Munkin <imun@tarantool.org> Cc: tarantool-patches@dev.tarantool.org Subject: Re: [Tarantool-patches] [PATCH 1/1] box: export box_session_push to the public C API Date: Wed, 8 Apr 2020 01:20:56 +0200 [thread overview] Message-ID: <88440e00-cda8-643e-8fa8-e79551bd7bdf@tarantool.org> (raw) In-Reply-To: <20200406231248.GB8215@tarantool.org> Hi! Thanks for the review! >> diff --git a/src/box/lua/console.c b/src/box/lua/console.c >> index 57e7e7f4f..ac353d012 100644 >> --- a/src/box/lua/console.c >> +++ b/src/box/lua/console.c >> @@ -410,6 +411,86 @@ port_lua_dump_plain(struct port *port, uint32_t *size) >> return result; >> } >> >> +/** >> + * A helper for port_msgpack_dump_plain() to safely work with Lua, >> + * being called via pcall(). >> + */ >> +static int >> +lua_port_msgpack_dump_plain(struct lua_State *L) >> +{ >> + const char *data = lua_touserdata(L, 1); >> + lua_pop(L, 1); >> + luamp_decode(L, luaL_msgpack_default, &data); > > I see both tag handle and prefix are the same as in port_lua_dump_plain, > so IMHO it would be great to introduce corresponding constants for them. > Furthermore, these values looks a bit cryptic to me, could you please > drop a few words nearby describing them? Can these values be changed in > the future? Tags are the standard YAML way of adding a type to an object. For details see lua_yaml_encode() comment, and http://yaml.org/spec/1.2/spec.html#tag/shorthand/. YAML allows to introduce your own tags, they just need to have a special syntax. For pushes we've chosen the tag you can see below. They can't be changed in future, since something may already depend on them. They are a part of the public API. Anyway, I now use the tag in only one place in v2 version, so no need to move it to a constant. >> + return lua_yaml_encode(L, luaL_yaml_default, "!push!", >> + "tag:tarantool.io/push,2018"); >> +} >> + >> +/** The same as port_lua plain dump, but from raw MessagePack. */ >> +const char * >> +port_msgpack_dump_plain(struct port *base, uint32_t *size) >> +{ >> + struct port_msgpack *port = (struct port_msgpack *) base; >> + struct lua_State *L = luaT_newthread(tarantool_L); > > It looks like an additional Lua coroutine is excess here and all > conversion from MessagePack to Lua can be done using the main one (i.e. > tarantool_L). I was afraid that in case of an OOM we will leave garbage in tarantool_L. But if you say we can use tarantool_L safely, then ok, I am not against that. It is just one another reason to start panic when the heap of Lua are out of memory, because otherwise data on tarantool_L will leak. On the other hand, the new thread will also leak, so probably this is a lose-lose situation, but with tarantool_L it is a little simpler. I rewrote that part significantly in v2. > I changed the patch a bit (diff is below) and tests > successfully passed on my machine. Did I miss something (maybe a yield > underneath luamp_decode / lua_yaml_encode)? > > ================================================================================ > > diff --git a/src/box/lua/console.c b/src/box/lua/console.c > index ac353d012..d5e0d193b 100644 > --- a/src/box/lua/console.c > +++ b/src/box/lua/console.c > @@ -457,7 +449,7 @@ port_msgpack_dump_plain(struct port *base, uint32_t *size) > */ > assert(lua_isstring(L, -1)); > diag_set(ClientError, ER_PROC_LUA, lua_tostring(L, -1)); > - goto error; > + return NULL; That is a bad idea, we still have garbage on tarantool_L. Need to drop it first. Anyway now I use cclosure, which automatically removes all leftovers from the stack. > } > assert(lua_gettop(L) - top == 1); > assert(lua_isstring(L, -1)); > ================================================================================ > >> + if (L == NULL) >> + return NULL; >> + /* >> + * Create a new thread and pop it immediately from the >> + * main stack. So as it would not stay there in case >> + * something would throw in this function. >> + */ >> + int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX); >> + /* >> + * MessagePack -> Lua -> YAML. The middle is not really >> + * needed here, but there is no MessagePack -> YAML >> + * encoder yet. >> + */ >> + int top = lua_gettop(L); >> + lua_pushcfunction(L, lua_port_msgpack_dump_plain); >> + lua_pushlightuserdata(L, (void *) port->data); >> + if (lua_pcall(L, 1, LUA_MULTRET, 0) != 0 || >> + lua_gettop(L) - top == 2) { >> + /* >> + * Nil and error string are pushed onto the stack >> + * if that was a YAML error. Only error string is >> + * pushed in case it was a Lua error. In both >> + * cases top of the stack is a string. >> + */ >> + assert(lua_isstring(L, -1)); >> + diag_set(ClientError, ER_PROC_LUA, lua_tostring(L, -1)); >> + goto error; >> + } >> + assert(lua_gettop(L) - top == 1); >> + assert(lua_isstring(L, -1)); >> + size_t len; >> + const char *result = lua_tolstring(L, -1, &len); >> + *size = (uint32_t) len; >> + /* >> + * The result string should be copied, because the stack, >> + * keeping the string, is going to be destroyed in the >> + * next lines. >> + * Can't use region, because somebody should free it, and >> + * its purpose is to free many objects at once. In this >> + * case only one string should be allocated and freed >> + * right after usage. Heap is fine for that. >> + * Can't use the global tarantool_lua_ibuf, because the >> + * plain dumper is used by session push, which yields in >> + * coio. And during that yield the ibuf can be reset by >> + * another fiber. >> + */ >> + char *buffer = (char *) strdup(result); > > Lua strings can contain NUL byte in the middle of the data area and it > is a good reason for using strndup here, considering you already obtain > the length earlier. Otherwise, it would be nice to drop a corresponding > comment about a contract guaranteeing the NUL byte doesn't occur in the > middle of the result value. YAML and Lua formatters escape 0 by writing '\0'. But anyway I agree, better use malloc + memcpy here. 'strndup' won't work, since it also stops when sees 0. Implemented in v2 and added a test. >> + if (buffer == NULL) { >> + diag_set(OutOfMemory, len + 1, "strdup", "buffer"); >> + goto error; >> + } >> + assert(port->buffer == NULL); >> + port->buffer = buffer; > > It seems to be more convenient to move the lines below to a separate > function (e.g port_msgpack_add_buffer). I have one rationale for it: you > make a strdup right here, but release the allocated memory in > port_msgpack_destroy. Such resource responsibility division seems to be > complex for the further maintainence. If you're against my proposal and > totally OK with the current approach, feel free to ignore. Agree, implemented in v2.
next prev parent reply other threads:[~2020-04-07 23:20 UTC|newest] Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-01-20 22:16 Vladislav Shpilevoy 2020-01-20 23:14 ` Vladislav Shpilevoy 2020-01-21 0:24 ` Alexander Turenko 2020-01-22 0:06 ` Vladislav Shpilevoy 2020-01-22 2:25 ` Alexander Turenko 2020-01-22 23:05 ` Vladislav Shpilevoy 2020-04-06 23:12 ` Igor Munkin 2020-04-07 23:20 ` Vladislav Shpilevoy [this message] 2020-04-06 23:40 ` Alexander Turenko 2020-03-30 20:42 ` Vladislav Shpilevoy 2020-03-30 21:03 ` Igor Munkin 2020-03-30 20:42 ` Vladislav Shpilevoy 2020-04-03 2:30 ` Nikita Pettik
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=88440e00-cda8-643e-8fa8-e79551bd7bdf@tarantool.org \ --to=v.shpilevoy@tarantool.org \ --cc=imun@tarantool.org \ --cc=tarantool-patches@dev.tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH 1/1] box: export box_session_push to the public C API' \ /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