Skip to content
Snippets Groups Projects
Commit a467d2fe authored by Roman Tsisyk's avatar Roman Tsisyk
Browse files

Fix #387: fiber local storage in Lua

parent 9cf2a7e1
No related branches found
No related tags found
No related merge requests found
......@@ -458,6 +458,7 @@ fiber_new(const char *name, void (*f) (va_list))
fiber->session = NULL;
fiber->flags = 0;
fiber->waiter = NULL;
fiber->lua_storage = -2; /* LUA_NOREF */;
fiber_set_name(fiber, name);
register_fid(fiber);
......
......@@ -111,6 +111,7 @@ struct fiber {
uint32_t flags;
struct fiber *waiter;
uint64_t cookie;
int lua_storage;
};
enum { FIBER_CALL_STACK = 16 };
......
......@@ -31,6 +31,7 @@
#include <fiber.h>
#include "lua/utils.h"
#include <session.h>
#include <scoped_guard.h>
extern "C" {
#include <lua.h>
......@@ -261,6 +262,12 @@ box_lua_fiber_run_detached(va_list ap)
LuarefGuard coro_guard(va_arg(ap, int));
struct lua_State *L = va_arg(ap, struct lua_State *);
SessionGuard session_guard(-1, 0);
auto storage_guard = make_scoped_guard([=] {
/* Destroy local storage */
if (fiber()->lua_storage != LUA_NOREF)
lua_unref(L, fiber()->lua_storage);
fiber()->lua_storage = LUA_NOREF;
});
try {
lbox_call(L, lua_gettop(L) - 1, LUA_MULTRET);
......@@ -357,6 +364,33 @@ lbox_fiber_name(struct lua_State *L)
}
}
static int
lbox_fiber_storage(struct lua_State *L)
{
struct fiber *f = lbox_checkfiber(L, 1);
if (f->lua_storage == LUA_NOREF) {
lua_newtable(L); /* create local storage on demand */
f->lua_storage = luaL_ref(L, LUA_REGISTRYINDEX);
}
lua_rawgeti(L, LUA_REGISTRYINDEX, f->lua_storage);
return 1;
}
static int
lbox_fiber_index(struct lua_State *L)
{
if (lua_gettop(L) < 2)
return 0;
if (lua_isstring(L, 2) && strcmp(lua_tostring(L, 2), "storage") == 0)
return lbox_fiber_storage(L);
/* Get value from metatable */
lua_getmetatable(L, 1);
lua_pushvalue(L, 2);
lua_gettable(L, -2);
return 1;
}
/**
* Yield to the sched fiber and sleep.
* @param[in] amount of time to sleep (double)
......@@ -456,6 +490,7 @@ static const struct luaL_reg lbox_fiber_meta [] = {
{"cancel", lbox_fiber_cancel},
{"status", lbox_fiber_status},
{"testcancel", lbox_fiber_testcancel},
{"__index", lbox_fiber_index},
{"__gc", lbox_fiber_gc},
{NULL, NULL}
};
......
......@@ -591,6 +591,123 @@ fiber.find(fib_id)
---
- null
...
--
-- Test local storage
--
fiber.self().storage
---
- []
...
fiber.self().storage.key = 48
---
...
fiber.self().storage.key
---
- 48
...
--# setopt delimiter ';'
function testfun(ch)
while fiber.self().storage.key == nil do
print('wait')
fiber.sleep(0)
end
ch:put(fiber.self().storage.key)
end;
---
...
--# setopt delimiter ''
ch = fiber.channel(1)
---
...
f = fiber.create(testfun, ch)
---
...
f.storage.key = 'some value'
---
...
ch:get()
---
- some value
...
ch:close()
---
...
ch = nil
---
...
fiber.self().storage.key -- our local storage is not affected by f
---
- 48
...
-- attempt to access local storage of dead fiber raises error
pcall(function(f) return f.storage end, f)
---
- false
- '[string "return pcall(function(f) return f.storage end..."]:1: the fiber is dead'
...
--
-- Test that local storage is garbage collected when fiber is died
--
ffi = require('ffi')
---
...
ch = fiber.channel(1)
---
...
--# setopt delimiter ';'
function testfun()
fiber.self().storage.x = ffi.gc(ffi.new('char[1]'),
function() ch:put('gc ok') end)
end;
---
...
--# setopt delimiter ''
f = fiber.create(testfun)
---
...
collectgarbage('collect')
---
- 0
...
ch:get()
---
- gc ok
...
ch:close()
---
...
ch = nil
---
...
--
-- Test that local storage is not garbage collected with fiber object
--
--# setopt delimiter ';'
function testfun(ch)
fiber.self().storage.x = 'ok'
collectgarbage('collect')
ch:put(fiber.self().storage.x or 'failed')
end;
---
...
--# setopt delimiter ''
ch = fiber.channel(1)
---
...
fiber.create(testfun, ch):status()
---
- dead
...
ch:get()
---
- ok
...
ch:close()
---
...
ch = nil
---
...
fiber = nil
---
...
......@@ -212,4 +212,67 @@ f:cancel()
fib_id = fiber.create(testfun):id()
fiber.find(fib_id):cancel()
fiber.find(fib_id)
--
-- Test local storage
--
fiber.self().storage
fiber.self().storage.key = 48
fiber.self().storage.key
--# setopt delimiter ';'
function testfun(ch)
while fiber.self().storage.key == nil do
print('wait')
fiber.sleep(0)
end
ch:put(fiber.self().storage.key)
end;
--# setopt delimiter ''
ch = fiber.channel(1)
f = fiber.create(testfun, ch)
f.storage.key = 'some value'
ch:get()
ch:close()
ch = nil
fiber.self().storage.key -- our local storage is not affected by f
-- attempt to access local storage of dead fiber raises error
pcall(function(f) return f.storage end, f)
--
-- Test that local storage is garbage collected when fiber is died
--
ffi = require('ffi')
ch = fiber.channel(1)
--# setopt delimiter ';'
function testfun()
fiber.self().storage.x = ffi.gc(ffi.new('char[1]'),
function() ch:put('gc ok') end)
end;
--# setopt delimiter ''
f = fiber.create(testfun)
collectgarbage('collect')
ch:get()
ch:close()
ch = nil
--
-- Test that local storage is not garbage collected with fiber object
--
--# setopt delimiter ';'
function testfun(ch)
fiber.self().storage.x = 'ok'
collectgarbage('collect')
ch:put(fiber.self().storage.x or 'failed')
end;
--# setopt delimiter ''
ch = fiber.channel(1)
fiber.create(testfun, ch):status()
ch:get()
ch:close()
ch = nil
fiber = nil
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment