diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-11 11:36:15 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-11 11:36:15 +0200 |
| commit | 84294d0b5462d0a05fb5d53df5a64f7ee624a726 (patch) | |
| tree | 16b79e505db565c5712b442cdfb3f3bb15be7884 | |
| parent | 17f98b33ed26338094a08d25e9fbf25b71c58f16 (diff) | |
| download | lanes-84294d0b5462d0a05fb5d53df5a64f7ee624a726.tar.gz lanes-84294d0b5462d0a05fb5d53df5a64f7ee624a726.tar.bz2 lanes-84294d0b5462d0a05fb5d53df5a64f7ee624a726.zip | |
Fixes and improvements to lane:join()
* fix: returns nil,cancel_error when lane was cancelled
* improvement: enforce non-nil first return value of lane body when using lane:join() to obtain the results
| -rw-r--r-- | docs/index.html | 36 | ||||
| -rw-r--r-- | src/lane.cpp | 14 | ||||
| -rw-r--r-- | tests/cancel.lua | 1 | ||||
| -rw-r--r-- | tests/cyclic.lua | 3 | ||||
| -rw-r--r-- | tests/fifo.lua | 4 | ||||
| -rw-r--r-- | tests/finalizer.lua | 1 | ||||
| -rw-r--r-- | tests/keeper.lua | 1 | ||||
| -rw-r--r-- | tests/linda_perf.lua | 2 | ||||
| -rw-r--r-- | tests/parallel_os_calls.lua | 8 | ||||
| -rw-r--r-- | tests/pingpong.lua | 4 | ||||
| -rw-r--r-- | tests/rupval.lua | 15 |
11 files changed, 61 insertions, 28 deletions
diff --git a/docs/index.html b/docs/index.html index 4e8cc25..400ceb1 100644 --- a/docs/index.html +++ b/docs/index.html | |||
| @@ -991,25 +991,23 @@ | |||
| 991 | </pre></td></tr></table> | 991 | </pre></td></tr></table> |
| 992 | 992 | ||
| 993 | <p> | 993 | <p> |
| 994 | <tt>timeout</tt> is an optional number >= 0 (the default if unspecified). | 994 | <tt>timeout</tt> is an optional number >= 0 (the default if unspecified).<br/> |
| 995 | <br/> | 995 | Waits until the lane finishes, or <tt>timeout</tt> seconds have passed.<br/> |
| 996 | Waits until the lane finishes, or <tt>timeout</tt> seconds have passed. | 996 | Unlike in reading the results in table fashion, errors are not propagated.<br/> |
| 997 | <br/> | 997 | Possible return values are: |
| 998 | Returns <tt>nil, "timeout"</tt> on timeout, <tt>nil,err,stack_tbl</tt> if the lane hit an error, <tt>nil, "killed"</tt> if forcefully killed, or the return values of the lane. | 998 | <ul> |
| 999 | <br/> | 999 | <li><tt>nil, "timeout"</tt> on timeout.</li> |
| 1000 | Unlike in reading the results in table fashion, errors are not propagated. | 1000 | <li> |
| 1001 | </p> | 1001 | <tt>nil,err,stack_tbl</tt> if the lane hit an error. The contents of <tt>stack_tbl</tt> change with <a href="#.error_trace_level"><tt>error_trace_level</tt></a>.<br/> |
| 1002 | 1002 | <ul> | |
| 1003 | <p> | 1003 | <li><tt>"minimal"</tt>: <tt>stack_tbl</tt> is <tt>nil</tt>.</li> |
| 1004 | <tt>stack_tbl</tt> is a table describing where the error was thrown. | 1004 | <li><tt>"basic"</tt>: <tt>stack_tbl</tt> is an array of <tt>"<filename>:<line>"</tt> strings. You can use <tt>table.concat()</tt> to format it to your liking (or just ignore it).</li> |
| 1005 | <br/> | 1005 | <li><tt>"extended"</tt>: <tt>stack_tbl</tt> is an array of tables containing info gathered with <tt>lua_getinfo()</tt> (<tt>"source"</tt>,<tt>"currentline"</tt>,<tt>"name"</tt>,<tt>"namewhat"</tt>,<tt>"what"</tt>).</li> |
| 1006 | In <tt>"extended"</tt> mode, <tt>stack_tbl</tt> is an array of tables containing info gathered with <tt>lua_getinfo()</tt> (<tt>"source"</tt>,<tt>"currentline"</tt>,<tt>"name"</tt>,<tt>"namewhat"</tt>,<tt>"what"</tt>). | 1006 | </ul> |
| 1007 | <br/> | 1007 | </li> |
| 1008 | In <tt>"basic"</tt> mode, <tt>stack_tbl</tt> is an array of <tt>"<filename>:<line>"</tt> strings. Use <tt>table.concat()</tt> to format it to your liking (or just ignore it). | 1008 | <li><tt>nil, "killed"</tt> if forcefully killed.</li> |
| 1009 | </p> | 1009 | <li>The return values of the lane function. If the first return value is <tt>nil</tt> (or there is no return value), an error is raised, to make sure you can tell timeout and error cases apart from successful return.</li> |
| 1010 | 1010 | </ul> | |
| 1011 | <p> | ||
| 1012 | If you use <tt>:join()</tt>, make sure your lane main function returns a non-nil value so you can tell timeout and error cases apart from succesful return (using the <tt>.status</tt> property may be risky, since it might change between a timed out join and the moment you read it). | ||
| 1013 | </p> | 1011 | </p> |
| 1014 | 1012 | ||
| 1015 | <table border=1 bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> | 1013 | <table border=1 bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> |
diff --git a/src/lane.cpp b/src/lane.cpp index 3bb98b9..eaf1c1e 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
| @@ -135,7 +135,11 @@ static LUAG_FUNC(thread_join) | |||
| 135 | switch (_lane->status) { | 135 | switch (_lane->status) { |
| 136 | case Lane::Done: | 136 | case Lane::Done: |
| 137 | { | 137 | { |
| 138 | bool const _calledFromIndex{ lua_toboolean(L_, lua_upvalueindex(1)) ? true : false }; // this upvalue doesn't exist when called from Lua | ||
| 138 | int const _n{ lua_gettop(_L2) }; // whole L2 stack | 139 | int const _n{ lua_gettop(_L2) }; // whole L2 stack |
| 140 | if (!_calledFromIndex && (_n == 0 || lua_isnil(_L2, 1))) { | ||
| 141 | raise_luaL_error(L_, "First return value must be non-nil when using join()"); | ||
| 142 | } | ||
| 139 | if ( | 143 | if ( |
| 140 | (_n > 0) && | 144 | (_n > 0) && |
| 141 | (InterCopyContext{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }.inter_move(_n) != InterCopyResult::Success) | 145 | (InterCopyContext{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }.inter_move(_n) != InterCopyResult::Success) |
| @@ -161,7 +165,11 @@ static LUAG_FUNC(thread_join) | |||
| 161 | break; | 165 | break; |
| 162 | 166 | ||
| 163 | case Lane::Cancelled: | 167 | case Lane::Cancelled: |
| 164 | _ret = 0; | 168 | // we should have a single value, kCancelError, in the stack of _L2 |
| 169 | LUA_ASSERT(L_, lua_gettop(_L2) == 1 && kCancelError.equals(_L2, 1)); | ||
| 170 | lua_pushnil(L_); // L_: lane nil | ||
| 171 | kCancelError.pushKey(L_); // L_: lane nil cancel_error | ||
| 172 | _ret = 2; | ||
| 165 | break; | 173 | break; |
| 166 | 174 | ||
| 167 | default: | 175 | default: |
| @@ -207,8 +215,10 @@ static int thread_index_number(lua_State* L_) | |||
| 207 | lua_pushinteger(L_, 0); // L_: lane n {uv} 0 | 215 | lua_pushinteger(L_, 0); // L_: lane n {uv} 0 |
| 208 | lua_pushboolean(L_, 1); // L_: lane n {uv} 0 true | 216 | lua_pushboolean(L_, 1); // L_: lane n {uv} 0 true |
| 209 | lua_rawset(L_, kUsr); // L_: lane n {uv} | 217 | lua_rawset(L_, kUsr); // L_: lane n {uv} |
| 218 | // tell join() that we are called from __index, to avoid raising an error if the first returned value is not nil | ||
| 219 | lua_pushboolean(L_, 1); // L_: lane n {uv} true | ||
| 210 | // wait until thread has completed, transfer everything from the lane's stack to our side | 220 | // wait until thread has completed, transfer everything from the lane's stack to our side |
| 211 | lua_pushcfunction(L_, LG_thread_join); // L_: lane n {uv} join | 221 | lua_pushcclosure(L_, LG_thread_join, 1); // L_: lane n {uv} join |
| 212 | lua_pushvalue(L_, kSelf); // L_: lane n {uv} join lane | 222 | lua_pushvalue(L_, kSelf); // L_: lane n {uv} join lane |
| 213 | lua_call(L_, 1, LUA_MULTRET); // lane:join() // L_: lane n {uv} ... | 223 | lua_call(L_, 1, LUA_MULTRET); // lane:join() // L_: lane n {uv} ... |
| 214 | switch (_lane->status) { | 224 | switch (_lane->status) { |
diff --git a/tests/cancel.lua b/tests/cancel.lua index 92ab439..42ae839 100644 --- a/tests/cancel.lua +++ b/tests/cancel.lua | |||
| @@ -15,6 +15,7 @@ local SLEEP = function(...) | |||
| 15 | local lanes = require "lanes" | 15 | local lanes = require "lanes" |
| 16 | local k, v = lanes.sleep(...) | 16 | local k, v = lanes.sleep(...) |
| 17 | assert(k == nil and v == "timeout") | 17 | assert(k == nil and v == "timeout") |
| 18 | return true | ||
| 18 | end | 19 | end |
| 19 | local sleeper = lanes.gen("*", sleeperBody)(...) | 20 | local sleeper = lanes.gen("*", sleeperBody)(...) |
| 20 | -- then wait for the lane to terminate | 21 | -- then wait for the lane to terminate |
diff --git a/tests/cyclic.lua b/tests/cyclic.lua index 656fde3..553d2a9 100644 --- a/tests/cyclic.lua +++ b/tests/cyclic.lua | |||
| @@ -30,6 +30,7 @@ local function lane1() | |||
| 30 | WR( "Via upvalue: ", same(a,b[1]), same(a[1],b) ) | 30 | WR( "Via upvalue: ", same(a,b[1]), same(a[1],b) ) |
| 31 | assert( a[1]==b ) | 31 | assert( a[1]==b ) |
| 32 | assert( b[1]==a ) | 32 | assert( b[1]==a ) |
| 33 | return true | ||
| 33 | end | 34 | end |
| 34 | local L1= lanes.gen( "io", lane1 )() | 35 | local L1= lanes.gen( "io", lane1 )() |
| 35 | -- ...running | 36 | -- ...running |
| @@ -40,6 +41,7 @@ local function lane2( aa, bb ) | |||
| 40 | WR( "Via parameters:", same(aa,bb[1]), same(aa[1],bb) ) | 41 | WR( "Via parameters:", same(aa,bb[1]), same(aa[1],bb) ) |
| 41 | assert( aa[1]==bb ) | 42 | assert( aa[1]==bb ) |
| 42 | assert( bb[1]==aa ) | 43 | assert( bb[1]==aa ) |
| 44 | return true | ||
| 43 | end | 45 | end |
| 44 | local L2= lanes.gen( "io", lane2 )( a, b ) | 46 | local L2= lanes.gen( "io", lane2 )( a, b ) |
| 45 | -- ...running | 47 | -- ...running |
| @@ -52,6 +54,7 @@ c.a= c | |||
| 52 | local function lane3( cc ) | 54 | local function lane3( cc ) |
| 53 | WR( "Directly recursive: ", same(cc, cc.a) ) | 55 | WR( "Directly recursive: ", same(cc, cc.a) ) |
| 54 | assert( cc and cc.a==cc ) | 56 | assert( cc and cc.a==cc ) |
| 57 | return true | ||
| 55 | end | 58 | end |
| 56 | local L3= lanes.gen("io", lane3)(c) | 59 | local L3= lanes.gen("io", lane3)(c) |
| 57 | 60 | ||
diff --git a/tests/fifo.lua b/tests/fifo.lua index d741931..e1bfeae 100644 --- a/tests/fifo.lua +++ b/tests/fifo.lua | |||
| @@ -82,4 +82,6 @@ print( B:receive( 2.0)) | |||
| 82 | -- by multiple threads (other parts will be copied but the 'linda' | 82 | -- by multiple threads (other parts will be copied but the 'linda' |
| 83 | -- handle is shared userdata and will thus point to the single place) | 83 | -- handle is shared userdata and will thus point to the single place) |
| 84 | lanes.timer_lane:cancel() -- hard cancel, 0 timeout | 84 | lanes.timer_lane:cancel() -- hard cancel, 0 timeout |
| 85 | lanes.timer_lane:join() \ No newline at end of file | 85 | local status, err = lanes.timer_lane:join() |
| 86 | assert(status == nil and err == lanes.cancel_error, "status="..tostring(status).." err="..tostring(err)) | ||
| 87 | print "TEST OK" | ||
diff --git a/tests/finalizer.lua b/tests/finalizer.lua index 2acc39d..3158c65 100644 --- a/tests/finalizer.lua +++ b/tests/finalizer.lua | |||
| @@ -39,6 +39,7 @@ local function lane(error_) | |||
| 39 | error(error_, 0) -- exception here; the value needs NOT be a string | 39 | error(error_, 0) -- exception here; the value needs NOT be a string |
| 40 | end | 40 | end |
| 41 | -- no exception | 41 | -- no exception |
| 42 | return true | ||
| 42 | end | 43 | end |
| 43 | 44 | ||
| 44 | -- | 45 | -- |
diff --git a/tests/keeper.lua b/tests/keeper.lua index 0220eba..2f731f0 100644 --- a/tests/keeper.lua +++ b/tests/keeper.lua | |||
| @@ -155,6 +155,7 @@ if true then | |||
| 155 | c.some= 3 | 155 | c.some= 3 |
| 156 | assert( c.some==3 ) | 156 | assert( c.some==3 ) |
| 157 | PRINT("c.some == " .. c.some) | 157 | PRINT("c.some == " .. c.some) |
| 158 | return true | ||
| 158 | end | 159 | end |
| 159 | 160 | ||
| 160 | PRINT("lane started") | 161 | PRINT("lane started") |
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua index 0b25989..4b0c005 100644 --- a/tests/linda_perf.lua +++ b/tests/linda_perf.lua | |||
| @@ -56,6 +56,7 @@ local eater = function( l, loop) | |||
| 56 | -- print "loop is over" | 56 | -- print "loop is over" |
| 57 | key, val = l:receive( "done") | 57 | key, val = l:receive( "done") |
| 58 | print("eater: done ("..val..")") | 58 | print("eater: done ("..val..")") |
| 59 | return true | ||
| 59 | end | 60 | end |
| 60 | 61 | ||
| 61 | -- ################################################################################################# | 62 | -- ################################################################################################# |
| @@ -73,6 +74,7 @@ local gobbler = function( l, loop, batch) | |||
| 73 | print "loop is over" | 74 | print "loop is over" |
| 74 | key, val = l:receive( "done") | 75 | key, val = l:receive( "done") |
| 75 | print("gobbler: done ("..val..")") | 76 | print("gobbler: done ("..val..")") |
| 77 | return true | ||
| 76 | end | 78 | end |
| 77 | 79 | ||
| 78 | -- ################################################################################################# | 80 | -- ################################################################################################# |
diff --git a/tests/parallel_os_calls.lua b/tests/parallel_os_calls.lua index 596053c..7e7de26 100644 --- a/tests/parallel_os_calls.lua +++ b/tests/parallel_os_calls.lua | |||
| @@ -1,10 +1,10 @@ | |||
| 1 | local lanes = require "lanes".configure() | 1 | local lanes = require "lanes".configure() |
| 2 | print( os.date()) | 2 | print( os.date()) |
| 3 | local linda = lanes.linda() | 3 | local linda = lanes.linda() |
| 4 | local l1 = lanes.gen("os,base", function() print "start sleeping" linda:receive(10, "null") print("finished_sleeping " .. os.date()) end)() | 4 | local l1 = lanes.gen("os,base", function() print "start sleeping" linda:receive(3, "null") print("finished_sleeping " .. os.date()) return true end)() |
| 5 | lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)() | 5 | lanes.gen("os,base", function() print "start sleeping" linda:receive(2, "null") print("finished_sleeping " .. os.date()) end)() |
| 6 | lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)() | 6 | lanes.gen("os,base", function() print "start sleeping" linda:receive(2, "null") print("finished_sleeping " .. os.date()) end)() |
| 7 | lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)() | 7 | lanes.gen("os,base", function() print "start sleeping" linda:receive(2, "null") print("finished_sleeping " .. os.date()) end)() |
| 8 | -- wait, else all lanes will get hard-cancelled at stat shutdown | 8 | -- wait, else all lanes will get hard-cancelled at stat shutdown |
| 9 | l1:join() | 9 | l1:join() |
| 10 | --[[ | 10 | --[[ |
diff --git a/tests/pingpong.lua b/tests/pingpong.lua index 30cd360..fdc60a6 100644 --- a/tests/pingpong.lua +++ b/tests/pingpong.lua | |||
| @@ -21,6 +21,7 @@ local pingpong = function(name, qr, qs, start) | |||
| 21 | q:send(qs, val) | 21 | q:send(qs, val) |
| 22 | count = count + 1 | 22 | count = count + 1 |
| 23 | end | 23 | end |
| 24 | return true | ||
| 24 | end | 25 | end |
| 25 | 26 | ||
| 26 | -- pingpong("L1", '0', '1', true) | 27 | -- pingpong("L1", '0', '1', true) |
| @@ -28,4 +29,5 @@ local t1, err1 = lanes.gen("*", pingpong)("L1", 'a', 'b', true) | |||
| 28 | local t2, err2 = lanes.gen("*", pingpong)("L2", 'b', 'a', false) | 29 | local t2, err2 = lanes.gen("*", pingpong)("L2", 'b', 'a', false) |
| 29 | 30 | ||
| 30 | t1:join() | 31 | t1:join() |
| 31 | t2:join() \ No newline at end of file | 32 | t2:join() |
| 33 | print "TEST OK" | ||
diff --git a/tests/rupval.lua b/tests/rupval.lua index 1079168..122e0ac 100644 --- a/tests/rupval.lua +++ b/tests/rupval.lua | |||
| @@ -25,6 +25,19 @@ end | |||
| 25 | 25 | ||
| 26 | local g = lanes.gen( "base", a) | 26 | local g = lanes.gen( "base", a) |
| 27 | 27 | ||
| 28 | local l = g(10) | 28 | local l = g(7) |
| 29 | local r = l:join() | 29 | local r = l:join() |
| 30 | assert(r == y) | ||
| 30 | print(r) | 31 | print(r) |
| 32 | |||
| 33 | local l = g(8) | ||
| 34 | local r = l:join() | ||
| 35 | assert(r == z) | ||
| 36 | print(r) | ||
| 37 | |||
| 38 | local l = g(9) | ||
| 39 | local r = l:join() | ||
| 40 | assert(r == x) | ||
| 41 | print(r) | ||
| 42 | |||
| 43 | print "TEST OK" | ||
