-
Mergen Imeev authored
This patch fixes memory leak in lbox_tuple_format_new(). Closes #4588 (cherry picked from commit 96199855)
Mergen Imeev authoredThis patch fixes memory leak in lbox_tuple_format_new(). Closes #4588 (cherry picked from commit 96199855)
misc.cc 7.68 KiB
/*
* Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "box/lua/misc.h"
#include "fiber.h" /* fiber->gc() */
#include <small/region.h>
#include "lua/utils.h"
#include "lua/msgpack.h"
#include "box/box.h"
#include "box/port.h"
#include "box/tuple.h"
#include "box/tuple_format.h"
#include "box/lua/tuple.h"
#include "mpstream.h"
static uint32_t CTID_STRUCT_TUPLE_FORMAT_PTR;
/** {{{ Miscellaneous utils **/
char *
lbox_encode_tuple_on_gc(lua_State *L, int idx, size_t *p_len)
{
struct region *gc = &fiber()->gc;
size_t used = region_used(gc);
struct mpstream stream;
mpstream_init(&stream, gc, region_reserve_cb, region_alloc_cb,
luamp_error, L);
luamp_encode_tuple(L, luaL_msgpack_default, &stream, idx);
mpstream_flush(&stream);
*p_len = region_used(gc) - used;
return (char *) region_join_xc(gc, *p_len);
}
/**
* Dump port_tuple content to Lua as a table. Used in box/port.c,
* but implemented here to eliminate port.c dependency on Lua.
*/
extern "C" void
port_tuple_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
{
struct port_tuple *port = port_tuple(base);
if (!is_flat)
lua_createtable(L, port->size, 0);
struct port_tuple_entry *pe = port->first;
for (int i = 0; pe != NULL; pe = pe->next) {
luaT_pushtuple(L, pe->tuple);
if (!is_flat)
lua_rawseti(L, -2, ++i);
}
}
extern "C" void
port_msgpack_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
{
(void) is_flat;
assert(is_flat == true);
struct port_msgpack *port = (struct port_msgpack *) base;
const char *args = port->data;
uint32_t arg_count = mp_decode_array(&args);
for (uint32_t i = 0; i < arg_count; i++)
luamp_decode(L, luaL_msgpack_default, &args);
}
/* }}} */
/** {{{ Lua/C implementation of index:select(): used only by Vinyl **/
static int
lbox_select(lua_State *L)
{
if (lua_gettop(L) != 6 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) ||
!lua_isnumber(L, 3) || !lua_isnumber(L, 4) || !lua_isnumber(L, 5)) {
return luaL_error(L, "Usage index:select(iterator, offset, "
"limit, key)");
}
uint32_t space_id = lua_tonumber(L, 1);
uint32_t index_id = lua_tonumber(L, 2);
int iterator = lua_tonumber(L, 3);
uint32_t offset = lua_tonumber(L, 4);
uint32_t limit = lua_tonumber(L, 5);
size_t key_len;
const char *key = lbox_encode_tuple_on_gc(L, 6, &key_len);
struct port port;
if (box_select(space_id, index_id, iterator, offset, limit,
key, key + key_len, &port) != 0) {
return luaT_error(L);
}
/*
* Lua may raise an exception during allocating table or pushing
* tuples. In this case `port' definitely will leak. It is possible to
* wrap lbox_port_to_table() to pcall(), but it was too expensive
* for this binding according to our benchmarks (~5% decrease).
* However, we tried to simulate this situation and LuaJIT finalizers
* table always crashed the first (can't be fixed with pcall).
* https://github.com/tarantool/tarantool/issues/1182
*/
port_dump_lua(&port, L, false);
port_destroy(&port);
return 1; /* lua table with tuples */
}
/* }}} */
/** {{{ Utils to work with tuple_format. **/
struct tuple_format *
lbox_check_tuple_format(struct lua_State *L, int narg)
{
uint32_t ctypeid;
struct tuple_format *format =
*(struct tuple_format **)luaL_checkcdata(L, narg, &ctypeid);
if (ctypeid != CTID_STRUCT_TUPLE_FORMAT_PTR) {
luaL_error(L, "Invalid argument: 'struct tuple_format *' "
"expected, got %s)",
lua_typename(L, lua_type(L, narg)));
}
return format;
}
static int
lbox_tuple_format_gc(struct lua_State *L)
{
struct tuple_format *format = lbox_check_tuple_format(L, 1);
tuple_format_unref(format);
return 0;
}
static int
lbox_push_tuple_format(struct lua_State *L, struct tuple_format *format)
{
struct tuple_format **ptr = (struct tuple_format **)
luaL_pushcdata(L, CTID_STRUCT_TUPLE_FORMAT_PTR);
*ptr = format;
tuple_format_ref(format);
lua_pushcfunction(L, lbox_tuple_format_gc);
luaL_setcdatagc(L, -2);
return 1;
}
static int
lbox_tuple_format_new(struct lua_State *L)
{
assert(CTID_STRUCT_TUPLE_FORMAT_PTR != 0);
int top = lua_gettop(L);
if (top == 0)
return lbox_push_tuple_format(L, tuple_format_runtime);
assert(top == 1 && lua_istable(L, 1));
uint32_t count = lua_objlen(L, 1);
if (count == 0)
return lbox_push_tuple_format(L, tuple_format_runtime);
size_t size = count * sizeof(struct field_def);
struct region *region = &fiber()->gc;
size_t region_svp = region_used(region);
struct field_def *fields =
(struct field_def *)region_alloc(region, size);
if (fields == NULL) {
diag_set(OutOfMemory, size, "region_alloc", "fields");
return luaT_error(L);
}
for (uint32_t i = 0; i < count; ++i) {
size_t len;
fields[i] = field_def_default;
lua_pushinteger(L, i + 1);
lua_gettable(L, 1);
lua_pushstring(L, "type");
lua_gettable(L, -2);
if (! lua_isnil(L, -1)) {
const char *type_name = lua_tolstring(L, -1, &len);
fields[i].type = field_type_by_name(type_name, len);
assert(fields[i].type != field_type_MAX);
}
lua_pop(L, 1);
lua_pushstring(L, "name");
lua_gettable(L, -2);
assert(! lua_isnil(L, -1));
const char *name = lua_tolstring(L, -1, &len);
fields[i].name = (char *)region_alloc(region, len + 1);
if (fields == NULL) {
diag_set(OutOfMemory, size, "region_alloc",
"fields[i].name");
region_truncate(region, region_svp);
return luaT_error(L);
}
memcpy(fields[i].name, name, len);
fields[i].name[len] = '\0';
lua_pop(L, 1);
lua_pop(L, 1);
}
struct tuple_dictionary *dict = tuple_dictionary_new(fields, count);
region_truncate(region, region_svp);
if (dict == NULL)
return luaT_error(L);
struct tuple_format *format =
tuple_format_new(&tuple_format_runtime->vtab, NULL, NULL, 0,
NULL, 0, 0, dict, false, false);
/*
* Since dictionary reference counter is 1 from the
* beginning and after creation of the tuple_format
* increases by one, we must decrease it once.
*/
tuple_dictionary_unref(dict);
if (format == NULL)
return luaT_error(L);
return lbox_push_tuple_format(L, format);
}
/* }}} */
void
box_lua_misc_init(struct lua_State *L)
{
static const struct luaL_Reg boxlib_internal[] = {
{"select", lbox_select},
{"new_tuple_format", lbox_tuple_format_new},
{NULL, NULL}
};
luaL_register(L, "box.internal", boxlib_internal);
lua_pop(L, 1);
int rc = luaL_cdef(L, "struct tuple_format;");
assert(rc == 0);
(void) rc;
CTID_STRUCT_TUPLE_FORMAT_PTR = luaL_ctypeid(L, "struct tuple_format *");
assert(CTID_STRUCT_TUPLE_FORMAT_PTR != 0);
}