From 84294d0b5462d0a05fb5d53df5a64f7ee624a726 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 11 Jun 2024 11:36:15 +0200 Subject: 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 --- docs/index.html | 36 +++++++++++++++++------------------- src/lane.cpp | 14 ++++++++++++-- tests/cancel.lua | 1 + tests/cyclic.lua | 3 +++ tests/fifo.lua | 4 +++- tests/finalizer.lua | 1 + tests/keeper.lua | 1 + tests/linda_perf.lua | 2 ++ tests/parallel_os_calls.lua | 8 ++++---- tests/pingpong.lua | 4 +++- 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 @@

- timeout is an optional number >= 0 (the default if unspecified). -
- Waits until the lane finishes, or timeout seconds have passed. -
- Returns nil, "timeout" on timeout, nil,err,stack_tbl if the lane hit an error, nil, "killed" if forcefully killed, or the return values of the lane. -
- Unlike in reading the results in table fashion, errors are not propagated. -

- -

-stack_tbl is a table describing where the error was thrown. -
- In "extended" mode, stack_tbl is an array of tables containing info gathered with lua_getinfo() ("source","currentline","name","namewhat","what"). -
- In "basic" mode, stack_tbl is an array of "<filename>:<line>" strings. Use table.concat() to format it to your liking (or just ignore it). -

- -

- If you use :join(), 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 .status property may be risky, since it might change between a timed out join and the moment you read it). + timeout is an optional number >= 0 (the default if unspecified).
+ Waits until the lane finishes, or timeout seconds have passed.
+ Unlike in reading the results in table fashion, errors are not propagated.
+ Possible return values are: +

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)
     switch (_lane->status) {
     case Lane::Done:
         {
+            bool const _calledFromIndex{ lua_toboolean(L_, lua_upvalueindex(1)) ? true : false }; // this upvalue doesn't exist when called from Lua
             int const _n{ lua_gettop(_L2) }; // whole L2 stack
+            if (!_calledFromIndex && (_n == 0 || lua_isnil(_L2, 1))) {
+                raise_luaL_error(L_, "First return value must be non-nil when using join()");
+            }
             if (
                 (_n > 0) &&
                 (InterCopyContext{ _lane->U, DestState{ L_ }, SourceState{ _L2 }, {}, {}, {}, {}, {} }.inter_move(_n) != InterCopyResult::Success)
@@ -161,7 +165,11 @@ static LUAG_FUNC(thread_join)
         break;
 
     case Lane::Cancelled:
-        _ret = 0;
+        // we should have a single value, kCancelError, in the stack of _L2
+        LUA_ASSERT(L_, lua_gettop(_L2) == 1 && kCancelError.equals(_L2, 1));
+        lua_pushnil(L_);                                                                           // L_: lane nil
+        kCancelError.pushKey(L_);                                                                  // L_: lane nil cancel_error
+        _ret = 2;
         break;
 
     default:
@@ -207,8 +215,10 @@ static int thread_index_number(lua_State* L_)
         lua_pushinteger(L_, 0);                                                                    // L_: lane n {uv} 0
         lua_pushboolean(L_, 1);                                                                    // L_: lane n {uv} 0 true
         lua_rawset(L_, kUsr);                                                                      // L_: lane n {uv}
+        // tell join() that we are called from __index, to avoid raising an error if the first returned value is not nil
+        lua_pushboolean(L_, 1);                                                                    // L_: lane n {uv} true
         // wait until thread has completed, transfer everything from the lane's stack to our side
-        lua_pushcfunction(L_, LG_thread_join);                                                     // L_: lane n {uv} join
+        lua_pushcclosure(L_, LG_thread_join, 1);                                                   // L_: lane n {uv} join
         lua_pushvalue(L_, kSelf);                                                                  // L_: lane n {uv} join lane
         lua_call(L_, 1, LUA_MULTRET); // lane:join()                                               // L_: lane n {uv} ...
         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(...)
 		local lanes = require "lanes"
 		local k, v = lanes.sleep(...)
 		assert(k == nil and v == "timeout")
+		return true
 	end
 	local sleeper = lanes.gen("*", sleeperBody)(...)
 	-- 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()
     WR( "Via upvalue: ", same(a,b[1]), same(a[1],b) )
     assert( a[1]==b )
     assert( b[1]==a )
+    return true
 end
 local L1= lanes.gen( "io", lane1 )()
     -- ...running
@@ -40,6 +41,7 @@ local function lane2( aa, bb )
     WR( "Via parameters:", same(aa,bb[1]), same(aa[1],bb) )
     assert( aa[1]==bb )
     assert( bb[1]==aa )
+    return true
 end
 local L2= lanes.gen( "io", lane2 )( a, b )
     -- ...running
@@ -52,6 +54,7 @@ c.a= c
 local function lane3( cc )
     WR( "Directly recursive: ", same(cc, cc.a) )
     assert( cc and cc.a==cc )
+    return true
 end
 local L3= lanes.gen("io", lane3)(c)
 
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))
 --       by multiple threads (other parts will be copied but the 'linda'
 --       handle is shared userdata and will thus point to the single place)
 lanes.timer_lane:cancel() -- hard cancel, 0 timeout
-lanes.timer_lane:join()
\ No newline at end of file
+local status, err = lanes.timer_lane:join()
+assert(status == nil and err == lanes.cancel_error, "status="..tostring(status).." err="..tostring(err))
+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_)
         error(error_, 0)    -- exception here; the value needs NOT be a string
     end
     -- no exception
+    return true
 end
 
 -- 
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
         c.some= 3
         assert( c.some==3 )
         PRINT("c.some == " .. c.some)
+        return true
     end
 
     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)
 	-- print "loop is over"
 	key, val = l:receive( "done")
 	print("eater: done ("..val..")")
+	return true
 end
 
 -- #################################################################################################
@@ -73,6 +74,7 @@ local gobbler = function( l, loop, batch)
 	print "loop is over"
 	key, val = l:receive( "done")
 	print("gobbler: done ("..val..")")
+	return true
 end
 
 -- #################################################################################################
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 @@
 local lanes = require "lanes".configure()
 print( os.date())
 local linda = lanes.linda()
-local l1 = lanes.gen("os,base", function() print "start sleeping" linda:receive(10, "null") print("finished_sleeping " .. os.date()) end)()
-lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)()
-lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)()
-lanes.gen("os,base", function() print "start sleeping" linda:receive(9, "null") print("finished_sleeping " .. os.date()) end)()
+local l1 = lanes.gen("os,base", function() print "start sleeping" linda:receive(3, "null") print("finished_sleeping " .. os.date()) return true end)()
+lanes.gen("os,base", function() print "start sleeping" linda:receive(2, "null") print("finished_sleeping " .. os.date()) end)()
+lanes.gen("os,base", function() print "start sleeping" linda:receive(2, "null") print("finished_sleeping " .. os.date()) end)()
+lanes.gen("os,base", function() print "start sleeping" linda:receive(2, "null") print("finished_sleeping " .. os.date()) end)()
 -- wait, else all lanes will get hard-cancelled at stat shutdown
 l1:join()
 --[[
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)
         q:send(qs, val)
         count = count + 1
     end
+    return true
 end
 
 -- pingpong("L1", '0', '1', true)
@@ -28,4 +29,5 @@ local t1, err1 = lanes.gen("*", pingpong)("L1", 'a', 'b', true)
 local t2, err2 = lanes.gen("*", pingpong)("L2", 'b', 'a', false)
 
 t1:join()
-t2:join()
\ No newline at end of file
+t2:join()
+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
 
 local g = lanes.gen( "base", a)
 
-local l = g(10)
+local l = g(7)
 local r = l:join()
+assert(r == y)
 print(r)
+
+local l = g(8)
+local r = l:join()
+assert(r == z)
+print(r)
+
+local l = g(9)
+local r = l:join()
+assert(r == x)
+print(r)
+
+print "TEST OK"
-- 
cgit v1.2.3-55-g6feb