diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2025-06-26 09:18:54 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2025-06-26 09:18:54 +0200 |
| commit | d7d756e30680bcff036118b47ac45b740e020ea2 (patch) | |
| tree | 3e2a26409154760d66092e6e04a9fcb4ad4ed02a /unit_tests/scripts/coro/yielding_function.lua | |
| parent | 4f5fa626c0279f5aefac477a29702c43a6754c5a (diff) | |
| download | lanes-d7d756e30680bcff036118b47ac45b740e020ea2.tar.gz lanes-d7d756e30680bcff036118b47ac45b740e020ea2.tar.bz2 lanes-d7d756e30680bcff036118b47ac45b740e020ea2.zip | |
Preparation for lane:close() and correct to-be-closed variables
* lane:join() can no longer be used to read yielded values
* same with lane indexing
* stub for lane:close(), similar to coroutine.close() (not implemented yet)
* preparing tests for to-be-closed variables in yielded coroutine lanes
* yielded lanes unlock and terminate properly at Lanes shutdown
Diffstat (limited to 'unit_tests/scripts/coro/yielding_function.lua')
| -rw-r--r-- | unit_tests/scripts/coro/yielding_function.lua | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/unit_tests/scripts/coro/yielding_function.lua b/unit_tests/scripts/coro/yielding_function.lua new file mode 100644 index 0000000..e7367ea --- /dev/null +++ b/unit_tests/scripts/coro/yielding_function.lua | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | local lanes = require "lanes" | ||
| 2 | |||
| 3 | local fixture = require "fixture" | ||
| 4 | lanes.finally(fixture.throwing_finalizer) | ||
| 5 | |||
| 6 | local utils = lanes.require "_utils" | ||
| 7 | local PRINT = utils.MAKE_PRINT() | ||
| 8 | |||
| 9 | -- a lane coroutine that yields back what got in, one element at a time | ||
| 10 | local yielder = function(...) | ||
| 11 | local utils = lanes.require "_utils" | ||
| 12 | local PRINT = utils.MAKE_PRINT() | ||
| 13 | PRINT "In lane" | ||
| 14 | for _i = 1, select('#', ...) do | ||
| 15 | local _val = select(_i, ...) | ||
| 16 | PRINT("yielding #", _i, _val) | ||
| 17 | local _ack = coroutine.yield(_val) | ||
| 18 | assert(_ack == _i) | ||
| 19 | end | ||
| 20 | return "done!" | ||
| 21 | end | ||
| 22 | |||
| 23 | -------------------------------------------------------------------------------------------------- | ||
| 24 | -- TEST: if we start a non-coroutine lane with a yielding function, we should get an error, right? | ||
| 25 | -------------------------------------------------------------------------------------------------- | ||
| 26 | if true then | ||
| 27 | local fun_g = lanes.gen("*", { name = 'auto' }, yielder) | ||
| 28 | local h = fun_g("hello", "world", "!") | ||
| 29 | local err, status, stack = h:join() | ||
| 30 | PRINT(err, status, stack) | ||
| 31 | -- the actual error message is not the same for Lua 5.1 | ||
| 32 | -- of course, it also has to be different for LuaJIT as well | ||
| 33 | -- also, LuaJIT prepends a file:line to the actual error message, which Lua5.1 does not. | ||
| 34 | local msgs = { | ||
| 35 | ["Lua 5.1"] = jit and "attempt to yield across C-call boundary" or "attempt to yield across metamethod/C-call boundary", | ||
| 36 | ["Lua 5.2"] = "attempt to yield from outside a coroutine", | ||
| 37 | ["Lua 5.3"] = "attempt to yield from outside a coroutine", | ||
| 38 | ["Lua 5.4"] = "attempt to yield from outside a coroutine" | ||
| 39 | } | ||
| 40 | local expected_msg = msgs[_VERSION] | ||
| 41 | PRINT("expected_msg = " .. expected_msg) | ||
| 42 | assert(err == nil and string.find(status, expected_msg, 1, true) and stack == nil, "status = " .. status) | ||
| 43 | end | ||
| 44 | |||
| 45 | -- the coroutine generator | ||
| 46 | local coro_g = lanes.coro("*", {name = "auto"}, yielder) | ||
| 47 | |||
| 48 | ------------------------------------------------------------------------------------------------- | ||
| 49 | -- TEST: we can resume as many times as the lane yields, then read the returned value on indexing | ||
| 50 | ------------------------------------------------------------------------------------------------- | ||
| 51 | if true then | ||
| 52 | -- launch coroutine lane | ||
| 53 | local h = coro_g("hello", "world", "!") | ||
| 54 | -- read the yielded values, sending back the expected index | ||
| 55 | assert(h:resume(1) == "hello") | ||
| 56 | assert(h:resume(2) == "world") | ||
| 57 | assert(h:resume(3) == "!") | ||
| 58 | -- the lane return value is available as usual | ||
| 59 | local r = h[1] | ||
| 60 | assert(r == "done!") | ||
| 61 | end | ||
| 62 | |||
| 63 | --------------------------------------------------------------------------------------------- | ||
| 64 | -- TEST: we can resume as many times as the lane yields, then read the returned value on join | ||
| 65 | --------------------------------------------------------------------------------------------- | ||
| 66 | if true then | ||
| 67 | -- launch coroutine lane | ||
| 68 | local h = coro_g("hello", "world", "!") | ||
| 69 | -- read the yielded values, sending back the expected index | ||
| 70 | assert(h:resume(1) == "hello") | ||
| 71 | assert(h:resume(2) == "world") | ||
| 72 | assert(h:resume(3) == "!") | ||
| 73 | -- the lane return value is available as usual | ||
| 74 | local s, r = h:join() | ||
| 75 | assert(h.status == "done" and s == true and r == "done!") | ||
| 76 | end | ||
| 77 | |||
| 78 | --------------------------------------------------------------------------------------------------- | ||
| 79 | -- TEST: if we join a yielded lane, we get a timeout, and we can resume as if we didn't try to join | ||
| 80 | --------------------------------------------------------------------------------------------------- | ||
| 81 | if true then | ||
| 82 | -- launch coroutine lane | ||
| 83 | local h = coro_g("hello", "world", "!") | ||
| 84 | -- read the first yielded value, sending back the expected index | ||
| 85 | assert(h:resume(1) == "hello") | ||
| 86 | -- join the lane. since it will reach a yield point, it remains suspended, and we should get a timeout | ||
| 87 | local b, r = h:join(0.5) | ||
| 88 | local s = h.status | ||
| 89 | assert(s == "suspended" and b == nil and r == "timeout", "got " .. s .. " " .. tostring(b) .. " " .. r) | ||
| 90 | -- trying to resume again should proceed normally, since nothing changed | ||
| 91 | assert(h:resume(2) == "world") | ||
| 92 | assert(h:resume(3) == "!") | ||
| 93 | -- the lane return value is available as usual | ||
| 94 | local s, r = h:join() | ||
| 95 | assert(h.status == "done" and s == true and r == "done!") | ||
| 96 | end | ||
| 97 | |||
| 98 | --------------------------------------------------------- | ||
| 99 | -- TEST: if we index yielded lane, we should get an error | ||
| 100 | --------------------------------------------------------- | ||
| 101 | -- TODO: implement this test! | ||
| 102 | |||
| 103 | |||
| 104 | ------------------------------------------------------------------------------------- | ||
| 105 | -- TEST: if we close yielded lane, we can join it and get the last yielded values out | ||
| 106 | ------------------------------------------------------------------------------------- | ||
| 107 | -- TODO: we need to implement lane:close() for that! | ||
