diff --git a/changelogs/unreleased/gh-5799-add-info-to-fiber-object.md b/changelogs/unreleased/gh-5799-add-info-to-fiber-object.md new file mode 100644 index 0000000000000000000000000000000000000000..3b5faad60b5e4cd555cde5756fcab4b56e7f58bc --- /dev/null +++ b/changelogs/unreleased/gh-5799-add-info-to-fiber-object.md @@ -0,0 +1,4 @@ +## feature/lua/fiber + +* Introduce `fiber_object:info()` to get `info` from fiber. Works as `require(fiber).info()` but only for one fiber. +* Introduce `fiber_object:csw()` to get `csw` from fiber (gh-5799). diff --git a/src/lua/fiber.c b/src/lua/fiber.c index 0800a1c269ea1a3ea3fa65b1a7e16df951150bdc..91898c283c29230b60f1c2bda7c3c877c18ef584 100644 --- a/src/lua/fiber.c +++ b/src/lua/fiber.c @@ -263,13 +263,10 @@ fiber_backtrace_cb(int frameno, void *frameret, const char *func, size_t offset, #endif static int -lbox_fiber_statof(struct fiber *f, void *cb_ctx, bool backtrace) +lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace) { struct lua_State *L = (struct lua_State *) cb_ctx; - luaL_pushuint64(L, f->fid); - lua_newtable(L); - lua_pushliteral(L, "name"); lua_pushstring(L, fiber_name(f)); lua_settable(L, -3); @@ -313,6 +310,16 @@ lbox_fiber_statof(struct fiber *f, void *cb_ctx, bool backtrace) lua_settable(L, -3); #endif /* ENABLE_BACKTRACE */ } + return 0; +} + +static int +lbox_fiber_statof(struct fiber *f, void *cb_ctx, bool backtrace) +{ + struct lua_State *L = cb_ctx; + luaL_pushuint64(L, f->fid); + lua_newtable(L); + lbox_fiber_statof_map(f, cb_ctx, backtrace); lua_settable(L, -3); return 0; } @@ -406,14 +413,10 @@ lbox_fiber_top_disable(struct lua_State *L) } #endif /* ENABLE_FIBER_TOP */ -/** - * Return fiber statistics. - */ -static int -lbox_fiber_info(struct lua_State *L) -{ #ifdef ENABLE_BACKTRACE - bool do_backtrace = true; +bool +lbox_do_backtrace(struct lua_State *L) +{ if (lua_istable(L, 1)) { lua_pushstring(L, "backtrace"); lua_gettable(L, 1); @@ -423,9 +426,21 @@ lbox_fiber_info(struct lua_State *L) lua_gettable(L, 1); } if (!lua_isnil(L, -1)) - do_backtrace = lua_toboolean(L, -1); + return lua_toboolean(L, -1); lua_pop(L, 1); } + return true; +} +#endif /* ENABLE_BACKTRACE */ + +/** + * Return fiber statistics. + */ +static int +lbox_fiber_info(struct lua_State *L) +{ +#ifdef ENABLE_BACKTRACE + bool do_backtrace = lbox_do_backtrace(L); if (do_backtrace) { lua_newtable(L); fiber_stat(lbox_fiber_statof_bt, L); @@ -527,6 +542,17 @@ lbox_fiber_new(struct lua_State *L) return 1; } +static struct fiber * +lbox_get_fiber(struct lua_State *L) +{ + if (lua_gettop(L) != 0) { + uint64_t *fid = luaL_checkudata(L, 1, fiberlib_name); + return fiber_find(*fid); + } else { + return fiber(); + } +} + /** * Get fiber status. * This follows the rules of Lua coroutine.status() function: @@ -540,16 +566,9 @@ lbox_fiber_new(struct lua_State *L) static int lbox_fiber_status(struct lua_State *L) { - struct fiber *f; - if (lua_gettop(L)) { - uint64_t fid = *(uint64_t *) - luaL_checkudata(L, 1, fiberlib_name); - f = fiber_find(fid); - } else { - f = fiber(); - } + struct fiber *f = lbox_get_fiber(L); const char *status; - if (f == NULL || f->fid == 0) { + if (f == NULL) { /* This fiber is dead. */ status = "dead"; } else if (f == fiber()) { @@ -563,6 +582,42 @@ lbox_fiber_status(struct lua_State *L) return 1; } +/** + * Get fiber info: number of context switches, backtrace, id, + * total memory, used memory. + */ +static int +lbox_fiber_object_info(struct lua_State *L) +{ + struct fiber *f = lbox_get_fiber(L); + if (f == NULL) + luaL_error(L, "the fiber is dead"); +#ifdef ENABLE_BACKTRACE + bool do_backtrace = lbox_do_backtrace(L); + if (do_backtrace) { + lua_newtable(L); + lbox_fiber_statof_map(f, L, true); + } else +#endif /* ENABLE_BACKTRACE */ + { + lua_newtable(L); + lbox_fiber_statof_map(f, L, false); + } + return 1; +} + +static int +lbox_fiber_csw(struct lua_State *L) +{ + struct fiber *f = lbox_get_fiber(L); + if (f == NULL) { + luaL_error(L, "the fiber is dead"); + } else { + lua_pushinteger(L, f->csw); + } + return 1; +} + /** * Get or set fiber name. * With no arguments, gets or sets the current fiber @@ -857,6 +912,8 @@ static const struct luaL_Reg lbox_fiber_meta [] = { {"name", lbox_fiber_name}, {"cancel", lbox_fiber_cancel}, {"status", lbox_fiber_status}, + {"info", lbox_fiber_object_info}, + {"csw", lbox_fiber_csw}, {"testcancel", lbox_fiber_testcancel}, {"__serialize", lbox_fiber_serialize}, {"__tostring", lbox_fiber_tostring}, diff --git a/test/app/gh-5799-fiber-info.result b/test/app/gh-5799-fiber-info.result new file mode 100644 index 0000000000000000000000000000000000000000..716ac9a246527b4706bf9b47da423911c968c02f --- /dev/null +++ b/test/app/gh-5799-fiber-info.result @@ -0,0 +1,80 @@ +-- test-run result file version 2 +test_run = require('test_run').new() + | --- + | ... +fiber = require('fiber') + | --- + | ... +json = require('json') + | --- + | ... + +function yielder(n) for i=1, n do fiber.yield() end end + | --- + | ... + +csw_check_counter = 0 + | --- + | ... +fibers = {} + | --- + | ... + +test_run:cmd('setopt delimiter ";"') + | --- + | - true + | ... +for i=1,100 do + fibers[i] = fiber.new(function() + local start_csw = fiber.info()[fiber.self():id()].csw + for j=1,10 do + fiber.yield() + if j + start_csw == fiber.self():csw() and + j + start_csw == fiber.self():info().csw then + csw_check_counter = csw_check_counter + 1 + end + end + end) + fibers[i]:set_joinable(true) +end; + | --- + | ... +for i=1,100 do + fibers[i]:join() +end; + | --- + | ... +test_run:cmd('setopt delimiter ""'); + | --- + | - true + | ... +csw_check_counter + | --- + | - 1000 + | ... + +cond = fiber.cond() + | --- + | ... +running = fiber.create(function() cond:wait() end) + | --- + | ... +json.encode(running:info()) == json.encode(fiber.info()[running:id()]) + | --- + | - true + | ... +cond:signal() + | --- + | ... +dead = fiber.create(function() end) + | --- + | ... + +dead:csw() + | --- + | - error: the fiber is dead + | ... +dead:info() + | --- + | - error: the fiber is dead + | ... diff --git a/test/app/gh-5799-fiber-info.test.lua b/test/app/gh-5799-fiber-info.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..588f760c40c8918f0184dac3ceb5b3508f1590bc --- /dev/null +++ b/test/app/gh-5799-fiber-info.test.lua @@ -0,0 +1,37 @@ +test_run = require('test_run').new() +fiber = require('fiber') +json = require('json') + +function yielder(n) for i=1, n do fiber.yield() end end + +csw_check_counter = 0 +fibers = {} + +test_run:cmd('setopt delimiter ";"') +for i=1,100 do + fibers[i] = fiber.new(function() + local start_csw = fiber.info()[fiber.self():id()].csw + for j=1,10 do + fiber.yield() + if j + start_csw == fiber.self():csw() and + j + start_csw == fiber.self():info().csw then + csw_check_counter = csw_check_counter + 1 + end + end + end) + fibers[i]:set_joinable(true) +end; +for i=1,100 do + fibers[i]:join() +end; +test_run:cmd('setopt delimiter ""'); +csw_check_counter + +cond = fiber.cond() +running = fiber.create(function() cond:wait() end) +json.encode(running:info()) == json.encode(fiber.info()[running:id()]) +cond:signal() +dead = fiber.create(function() end) + +dead:csw() +dead:info()