diff --git a/core/fiber.m b/core/fiber.m index 32f36d7be157bb2afe3e3b52b58e69782e4e2626..6dba8ff44bb7db5e3af3d828a407b2d91d596a66 100644 --- a/core/fiber.m +++ b/core/fiber.m @@ -251,6 +251,22 @@ fiber_yield(void) coro_transfer(&caller->coro.ctx, &callee->coro.ctx); } +/** + * Return true if the current fiber is a callee of fiber f, + * false otherwise. + */ +bool +fiber_is_caller(struct fiber *f) +{ + /* 'Unwinding' the fiber stack. */ + for (struct fiber **sp_ptr = sp; sp_ptr > call_stack; --sp_ptr) { + if (f == *sp_ptr) + return true; + } + return false; +} + + /** * @note: this is a cancellation point (@sa fiber_testcancel()) */ diff --git a/core/tarantool_lua.m b/core/tarantool_lua.m index 9f02772362b3c340d9df65814fb3e9ac50646d63..a4ade18da29ad6842d638b927bc7944db413197f 100644 --- a/core/tarantool_lua.m +++ b/core/tarantool_lua.m @@ -517,6 +517,40 @@ lbox_fiber_yield(struct lua_State *L) return lua_gettop(L); } +/** + * Get fiber status. + * This follows the rules of Lua coroutine.status() function: + * Returns the status of fibier, as a string: + * - "running", if the fiber is running (that is, it called status); + * - "suspended", if the fiber is suspended in a call to yield(), + * or if it has not started running yet; + * - "normal" if the fiber is active but not running (that is, + * it has resumed another fiber); + * - "dead" if the fiber has finished its body function, or if it + * has stopped with an error. + */ +static int +lbox_fiber_status(struct lua_State *L) +{ + struct fiber *f = lbox_checkfiber(L, 1); + const char *status; + if (f->fid == 0) { + /* This fiber is dead. */ + status = "dead"; + } else if (f == fiber) { + /* The fiber is the current running fiber. */ + status = "running"; + } else if (fiber_is_caller(f)) { + /* The fiber is current fiber's caller. */ + status = "normal"; + } else { + /* None of the above: must be suspended. */ + status = "suspended"; + } + lua_pushstring(L, status); + return 1; +} + /** * Detach the current fiber. */ @@ -615,6 +649,7 @@ static const struct luaL_reg fiberlib[] = { {"create", lbox_fiber_create}, {"resume", lbox_fiber_resume}, {"yield", lbox_fiber_yield}, + {"status", lbox_fiber_status}, {"detach", lbox_fiber_detach}, {NULL, NULL} }; diff --git a/include/fiber.h b/include/fiber.h index 9b073b74253fd1bf0f325a7f300a17ab7c42279e..66f4148a02a27e188dc0f5f8bb4e98eaddf7b88a 100644 --- a/include/fiber.h +++ b/include/fiber.h @@ -148,6 +148,9 @@ void fiber_yield(void); void fiber_destroy_all(); +bool +fiber_is_caller(struct fiber *f); + struct msg *read_inbox(void); ssize_t fiber_bread(struct tbuf *, size_t v); diff --git a/test/box/box_fiber.lua b/test/box/box_fiber.lua new file mode 100644 index 0000000000000000000000000000000000000000..60d3977eea500e5cef8e65fad2f83e8633d2e034 --- /dev/null +++ b/test/box/box_fiber.lua @@ -0,0 +1,78 @@ +-- -------------------------------------------------------------------------- -- +-- Local functions +-- -------------------------------------------------------------------------- -- + +-- printer task fiber +local printer_task + +-- tester task fiber +local tester_task + + +-- -------------------------------------------------------------------------- -- +-- printer task routines +-- -------------------------------------------------------------------------- -- + +-- odd printer +local function odd(x) + print('A: odd ', x) + box.fiber.yield(x) + print('B: odd ', x) +end + +-- even printer +local function even(x) + print('C: even ', x) + if x == 2 then + return x + end + print('D: even ', x) +end + +-- printer task routine main function +local function printer_task_routine(x) + print("printer: tester status = ", box.fiber.status(tester_task)) + print("printer: printer status = ", box.fiber.status(printer_task)) + for i = 1, x do + if i == 3 then + box.fiber.yield(-1) + end + if i % 2 == 0 then + even(i) + else + odd(i) + end + end +end + + +-- -------------------------------------------------------------------------- -- +-- tester task routines +-- -------------------------------------------------------------------------- -- + +-- tester task routine main function +local function tester_task_routine() + printer_task = box.fiber.create(printer_task_routine) + print("tester: status(tester) = ", box.fiber.status(tester_task)) + print("tester: status(tester) = ", box.fiber.status(printer_task)) + + count = 1 + while box.fiber.status(printer_task) ~= "dead" do + print("count: ", count) + value = box.fiber.resume(printer_task, 5) + print("status: ", box.fiber.status(printer_task)) + count = count + 1 + end +end + + +-- -------------------------------------------------------------------------- -- +-- Test functions +-- -------------------------------------------------------------------------- -- + +-- run fiber test +function box_fiber_run_test() + -- run tester + tester_task = box.fiber.create(tester_task_routine) + box.fiber.resume(tester_task) +end diff --git a/test/box/lua.result b/test/box/lua.result index 500697e8a36e5c7d8bbe28b938bc8aa3abdc2bed..0fbc3a9738552decfeeede1560e1ed6e90fd5458 100644 --- a/test/box/lua.result +++ b/test/box/lua.result @@ -844,3 +844,55 @@ lua os.execute('ls') --- error: 'Lua error: [string "return os.execute(''ls'')"]:1: attempt to call field ''execute'' (a nil value)' ... + +# +# box.fiber test (create, resume, yield, status) +# + +lua dofile(...) +--- +... + +# test box.fiber.status functions: invalid arguments + +lua box.fiber.status(1) +--- +error: 'Lua error: bad argument #1 to ''?'' (box.fiber expected, got number)' +... +lua box.fiber.status('fafa-gaga') +--- +error: 'Lua error: bad argument #1 to ''?'' (box.fiber expected, got string)' +... +lua box.fiber.status(nil) +--- +error: 'Lua error: bad argument #1 to ''?'' (box.fiber expected, got nil)' +... + +# run fiber's test + +lua box_fiber_run_test() +--- +tester: status(tester) = running +tester: status(tester) = suspended +count: 1 +printer: tester status = normal +printer: printer status = running +A: odd 1 +status: suspended +count: 2 +B: odd 1 +C: even 2 +status: suspended +count: 3 +A: odd 3 +status: suspended +count: 4 +B: odd 3 +C: even 4 +D: even 4 +A: odd 5 +status: suspended +count: 5 +B: odd 5 +status: dead +... diff --git a/test/box/lua.test b/test/box/lua.test index 350c34ed951f938197ed9bd8c008e122166588e9..627bec1ba608b07bd816e7a33d8ebfed5cabb94f 100644 --- a/test/box/lua.test +++ b/test/box/lua.test @@ -250,3 +250,28 @@ print """ # Lua provides access to os.execute() """ exec admin "lua os.execute('ls')" + + +print """ +# +# box.fiber test (create, resume, yield, status) +# +""" + +box_fiber_lua = os.path.abspath("box/box_fiber.lua") +# don't log the path name +sys.stdout.push_filter("lua dofile(.*)", "lua dofile(...)") +exec admin "lua dofile('{0}')".format(box_fiber_lua) +sys.stdout.pop_filter() + +print """ +# test box.fiber.status functions: invalid arguments +""" +exec admin "lua box.fiber.status(1)" +exec admin "lua box.fiber.status('fafa-gaga')" +exec admin "lua box.fiber.status(nil)" + +print """ +# run fiber's test +""" +exec admin "lua box_fiber_run_test()"