From 9da7e03e88e29b8d38ff8368fda1da4d87f6460b Mon Sep 17 00:00:00 2001 From: Iskander Sagitov <i.sagitov@tarantool.org> Date: Thu, 13 May 2021 11:42:36 +0300 Subject: [PATCH] fiber: introduce fiber_o:info() and fiber_o:csw() If you want to get information or get csw (Context SWitch) of some fiber you need to call fiber.info(), but it creates table with information about all the fibers. This patch introduces fiber_object:info() and fiber_object:csw() - functions to solve this problem. Closes #5799 @TarantoolBot document Title: introduce fiber_object:info() and fiber_object:csw() ``` -- fiber_object:info() is the same as fiber.info(), but show information only about one alive fiber. -- fiber_object:csw() show csw (Context SWitch) of alive fiber. ``` --- .../gh-5799-add-info-to-fiber-object.md | 4 + src/lua/fiber.c | 99 +++++++++++++++---- test/app/gh-5799-fiber-info.result | 80 +++++++++++++++ test/app/gh-5799-fiber-info.test.lua | 37 +++++++ 4 files changed, 199 insertions(+), 21 deletions(-) create mode 100644 changelogs/unreleased/gh-5799-add-info-to-fiber-object.md create mode 100644 test/app/gh-5799-fiber-info.result create mode 100644 test/app/gh-5799-fiber-info.test.lua 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 0000000000..3b5faad60b --- /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 0800a1c269..91898c283c 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 0000000000..716ac9a246 --- /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 0000000000..588f760c40 --- /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() -- GitLab