[tarantool-patches] [PATCH v2 08/10] session: introduce text box.session.push
Konstantin Osipov
kostja at tarantool.org
Thu May 10 22:27:54 MSK 2018
* Vladislav Shpilevoy <v.shpilevoy at tarantool.org> [18/04/20 16:31]:
> +/**
> + * Push a tagged YAML document into a console socket.
> + * @param session Console session.
> + * @param port Port with YAML to push.
> + *
> + * @retval 0 Success.
> + * @retval -1 Error.
> + */
> +static int
> +console_session_push(struct session *session, struct port *port)
> +{
> + assert(session_vtab_registry[session->type].push ==
> + console_session_push);
> + uint32_t text_len;
> + const char *text = port_dump_plain(port, &text_len);
> + if (text == NULL)
> + return -1;
> + int fd = session_fd(session);
> + while (text_len > 0) {
> + while (coio_wait(fd, COIO_WRITE,
> + TIMEOUT_INFINITY) != COIO_WRITE);
Nitpick: the socket is ready in 99% of cases, there is no reason
to call coio_wait() unless you get EINTR.
Second, why the choice of fio_writev() rather than write() or
send?
What is wrong with coio_writev or similar?
> + const struct iovec iov = {
> + .iov_base = (void *) text,
> + .iov_len = text_len
> + };
> + ssize_t rc = fio_writev(fd, &iov, 1);
> + if (rc < 0)
> + return -1;
> + text_len -= rc;
> + text += rc;
> + }
> + return 0;
> +}
> @@ -92,13 +93,27 @@ local text_connection_mt = {
> --
> eval = function(self, text)
> text = text..'$EOF$\n'
> - if self:write(text) then
> + if not self:write(text) then
> + error(self:set_error())
> + end
> + while true do
> local rc = self:read()
> - if rc then
> + if not rc then
> + break
> + end
> + local handle, prefix = yaml.decode_tag(rc)
> + assert(handle or not prefix)
> + if not handle then
> + -- Can not fail - tags are encoded with no
> + -- user participation and are correct always.
> + assert(not prefix)
In Lua, asserts take CPU time. Please don't use them unless in a
test.
> return rc
> end
> + if handle == PUSH_TAG_HANDLE and self.print_f then
> + self.print_f(rc)
> + end
> end
> - error(self:set_error())
> + return rc
> end,
> --
> -- Make the connection be in error state, set error
> @@ -121,15 +136,18 @@ local text_connection_mt = {
> -- netbox-like object.
> -- @param connection Socket to wrap.
> -- @param url Parsed destination URL.
> +-- @param print_f Function to print push messages.
> +--
> -- @retval nil, err Error, and err contains an error message.
> -- @retval not nil Netbox-like object.
> --
> -local function wrap_text_socket(connection, url)
> +local function wrap_text_socket(connection, url, print_f)
> local conn = setmetatable({
> _socket = connection,
> state = 'active',
> host = url.host or 'localhost',
> port = url.service,
> + print_f = print_f,
> }, text_connection_mt)
> if not conn:write('require("console").delimiter("$EOF$")\n') or
> not conn:read() then
> @@ -369,7 +387,8 @@ local function connect(uri, opts)
> end
> local remote
> if greeting.protocol == 'Lua console' then
> - remote = wrap_text_socket(connection, u)
> + remote = wrap_text_socket(connection, u,
> + function(msg) self:print(msg) end)
> else
> opts = {
> connect_timeout = opts.timeout,
> diff --git a/src/box/lua/session.c b/src/box/lua/session.c
> index 5fe5f08d4..306271809 100644
> --- a/src/box/lua/session.c
> +++ b/src/box/lua/session.c
> @@ -41,6 +41,8 @@
> #include "box/session.h"
> #include "box/user.h"
> #include "box/schema.h"
> +#include "box/port.h"
> +#include "box/lua/console.h"
>
> static const char *sessionlib_name = "box.session";
>
> @@ -355,6 +357,52 @@ lbox_push_on_access_denied_event(struct lua_State *L, void *event)
> return 3;
> }
>
> +/**
> + * Port to push a message from Lua.
> + */
> +struct lua_push_port {
> + const struct port_vtab *vtab;
> + /**
> + * Lua state, containing data to dump on top of the stack.
> + */
> + struct lua_State *L;
> +};
> +
> +static const char *
> +lua_push_port_dump_plain(struct port *port, uint32_t *size);
> +
> +static const struct port_vtab lua_push_port_vtab = {
> + .dump_msgpack = NULL,
> + /*
> + * Dump_16 has no sense, since push appears since 1.10
> + * protocol.
> + */
> + .dump_msgpack_16 = NULL,
> + .dump_plain = lua_push_port_dump_plain,
> + .destroy = NULL,
> +};
> +
> +static const char *
> +lua_push_port_dump_plain(struct port *port, uint32_t *size)
> +{
> + struct lua_push_port *lua_port = (struct lua_push_port *) port;
> + assert(lua_port->vtab == &lua_push_port_vtab);
> + struct lua_State *L = lua_port->L;
> + int rc = console_encode_push(L);
> + if (rc == 2) {
> + assert(lua_isnil(L, -2));
> + assert(lua_isstring(L, -1));
> + diag_set(ClientError, ER_PROC_LUA, lua_tostring(L, -1));
> + return NULL;
> + }
> + assert(rc == 1);
> + assert(lua_isstring(L, -1));
> + size_t len;
> + const char *result = lua_tolstring(L, -1, &len);
> + *size = (uint32_t) len;
> + return result;
> +}
> +
> /**
> * Push a message using a protocol, depending on a session type.
> * @param data Data to push, first argument on a stack.
> @@ -367,7 +415,11 @@ lbox_session_push(struct lua_State *L)
> if (lua_gettop(L) != 1)
> return luaL_error(L, "Usage: box.session.push(data)");
>
> - if (session_push(current_session(), NULL) != 0) {
> + struct lua_push_port port;
> + port.vtab = &lua_push_port_vtab;
Why do you need a separate port?
And why do you need to create a port for every push? Can't you
reuse the same port as is used for the Lua call itself?
> + port.L = L;
> +
> + if (session_push(current_session(), (struct port *) &port) != 0) {
> lua_pushnil(L);
> luaT_pusherror(L, box_error_last());
> return 2;
> diff --git a/src/box/port.c b/src/box/port.c
> index 255eb732c..f9b655840 100644
--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.io - www.twitter.com/kostja_osipov
More information about the Tarantool-patches
mailing list