From c0ac37c79f3d9fbc12b99541f58caaca8667182c Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 15 Apr 2025 12:00:23 +0200 Subject: Fix some tests (kinda) * split tasking_cancelling in two * add a timer in basic and tasking_cancelling as a temporary workaround for a deadlock in mingw ucrt64 builds --- .vscode/launch.json | 2 +- Makefile | 4 +- tests/basic.lua | 33 +++++--- unit_tests/UnitTests.vcxproj | 1 + unit_tests/UnitTests.vcxproj.filters | 3 + unit_tests/lane_tests.cpp | 2 + unit_tests/scripts/lane/tasking_cancelling.lua | 89 ++++++---------------- .../scripts/lane/tasking_cancelling_with_hook.lua | 68 +++++++++++++++++ 8 files changed, 120 insertions(+), 82 deletions(-) create mode 100644 unit_tests/scripts/lane/tasking_cancelling_with_hook.lua diff --git a/.vscode/launch.json b/.vscode/launch.json index c4de37e..a2781fd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "args": [ //"--list-tests", "--rng-seed 0", - "-s scripted_tests.legacy.basic" + "-s scripted_tests.lane.tasking_cancelling" ], "stopAtEntry": true, "cwd": "${workspaceFolder}", diff --git a/Makefile b/Makefile index 2d7694c..98fa1ac 100644 --- a/Makefile +++ b/Makefile @@ -80,12 +80,12 @@ build_DUE: run_unit_tests: build_lanes build_unit_tests build_DUE @echo ========================================================================================= $(_PREFIX) $(_UNITTEST_TARGET) --list-tests - $(_PREFIX) $(_UNITTEST_TARGET) --rng-seed 0 -s + $(_PREFIX) $(_UNITTEST_TARGET) --rng-seed 0 -s scripted_tests.lane.tasking_cancelling debug_unit_tests: build_lanes build_unit_tests build_DUE @echo ========================================================================================= $(_PREFIX) $(_UNITTEST_TARGET) --list-tests - $(_PREFIX) gdb --args $(_UNITTEST_TARGET) --list-tests --rng-seed 0 -s "scripted_tests.legacy.basic" + $(_PREFIX) gdb --args $(_UNITTEST_TARGET) --rng-seed 0 -s scripted_tests.lane.tasking_cancelling clean: cd src && $(MAKE) -f Lanes.makefile clean diff --git a/tests/basic.lua b/tests/basic.lua index 068dc25..170e821 100644 --- a/tests/basic.lua +++ b/tests/basic.lua @@ -173,29 +173,38 @@ for k, v in pairs(limited:dump()) do end local wait_send = function() local a,b - set_finalizer(function() print("wait_send", a, b) end) + set_finalizer(function(err, stack_tbl) print("wait_send", a, b, " -> ", tostring(err)) end) + print("in wait_send") a,b = limited:send("key", "bybye") -- infinite timeout, returns only when lane is cancelled end local wait_send_lane = lanes.gen("*", { name = 'auto' }, wait_send)() -repeat until wait_send_lane.status == "waiting" -print "wait_send_lane is waiting" +repeat + io.stderr:write('!') + -- currently mingw64 builds can deadlock if we cancel the lane too early (before the linda blocks, at it causes the linda condvar not to be signalled) + lanes.sleep(0.1) +until wait_send_lane.status == "waiting" +PRINT "wait_send_lane is waiting" wait_send_lane:cancel() -- hard cancel, 0 timeout repeat until wait_send_lane.status == "cancelled" -print "wait_send_lane is cancelled" +PRINT "wait_send_lane is cancelled" --################################################]] local wait_receive = function() local k, v - set_finalizer(function() print("wait_receive", k, v) end) + set_finalizer(function(err, stack_tbl) print("wait_receive", k, v, " -> ", tostring(err)) end) k, v = limited:receive("dummy") -- infinite timeout, returns only when lane is cancelled end local wait_receive_lane = lanes.gen("*", { name = 'auto' }, wait_receive)() -repeat until wait_receive_lane.status == "waiting" -print "wait_receive_lane is waiting" +repeat + io.stderr:write('!') + -- currently mingw64 builds can deadlock if we cancel the lane too early (before the linda blocks, at it causes the linda condvar not to be signalled) + lanes.sleep(0.1) +until wait_receive_lane.status == "waiting" +PRINT "wait_receive_lane is waiting" wait_receive_lane:cancel() -- hard cancel, 0 timeout repeat until wait_receive_lane.status == "cancelled" -print "wait_receive_lane is cancelled" +PRINT "wait_receive_lane is cancelled" --################################################]] local wait_receive_batched = function() local k, v1, v2 @@ -205,10 +214,10 @@ end local wait_receive_batched_lane = lanes.gen("*", { name = 'auto' }, wait_receive_batched)() repeat until wait_receive_batched_lane.status == "waiting" -print "wait_receive_batched_lane is waiting" +PRINT "wait_receive_batched_lane is waiting" wait_receive_batched_lane:cancel() -- hard cancel, 0 timeout repeat until wait_receive_batched_lane.status == "cancelled" -print "wait_receive_batched_lane is cancelled" +PRINT "wait_receive_batched_lane is cancelled" --################################################]] -- ################################################################################################## @@ -437,7 +446,7 @@ local function chunk2(linda) assert(config.strip_functions and info.short_src=="?" or string.match(info.short_src, "^.*basic.lua$"), "bad info.short_src") -- These vary so let's not be picky (they're there..) -- - assert(info.linedefined == 422, "bad linedefined") -- start of 'chunk2' + assert(info.linedefined == 431, "bad linedefined") -- start of 'chunk2' assert(config.strip_functions and info.currentline==-1 or info.currentline > info.linedefined, "bad currentline") -- line of 'debug.getinfo' assert(info.lastlinedefined > info.currentline, "bad lastlinedefined") -- end of 'chunk2' local k,func= linda:receive("down") @@ -460,7 +469,7 @@ linda:send("down", function(linda) linda:send("up", "ready!") end, -- local k,s= linda:receive(1, "up") if t2.status == "error" then - print("t2 error: " , t2:join()) + PRINT("t2 error: " , t2:join()) end PRINT(s) assert(s=="ready!") diff --git a/unit_tests/UnitTests.vcxproj b/unit_tests/UnitTests.vcxproj index e78e32a..e1370c3 100644 --- a/unit_tests/UnitTests.vcxproj +++ b/unit_tests/UnitTests.vcxproj @@ -967,6 +967,7 @@ + diff --git a/unit_tests/UnitTests.vcxproj.filters b/unit_tests/UnitTests.vcxproj.filters index 3b45009..b0ce216 100644 --- a/unit_tests/UnitTests.vcxproj.filters +++ b/unit_tests/UnitTests.vcxproj.filters @@ -113,5 +113,8 @@ Catch2 + + Scripts\lane + \ No newline at end of file diff --git a/unit_tests/lane_tests.cpp b/unit_tests/lane_tests.cpp index 0a392d8..1367ae5 100644 --- a/unit_tests/lane_tests.cpp +++ b/unit_tests/lane_tests.cpp @@ -332,6 +332,7 @@ MAKE_TEST_CASE(lane, cooperative_shutdown, AssertNoLuaError) MAKE_TEST_CASE(lane, uncooperative_shutdown, AssertWarns) #endif // LUA_VERSION_NUM MAKE_TEST_CASE(lane, tasking_basic, AssertNoLuaError) +MAKE_TEST_CASE(lane, tasking_cancelling_with_hook, AssertNoLuaError) MAKE_TEST_CASE(lane, tasking_cancelling, AssertNoLuaError) MAKE_TEST_CASE(lane, tasking_comms_criss_cross, AssertNoLuaError) MAKE_TEST_CASE(lane, tasking_communications, AssertNoLuaError) @@ -352,6 +353,7 @@ TEST_CASE("lanes.scripted_tests") FileRunnerParam{ PUC_LUA_ONLY("lane/cooperative_shutdown"), TestType::AssertNoLuaError }, // 0 FileRunnerParam{ "lane/uncooperative_shutdown", TestType::AssertWarns }, FileRunnerParam{ "lane/tasking_basic", TestType::AssertNoLuaError }, // 2 + FileRunnerParam{ "lane/tasking_cancelling_with_hook", TestType::AssertNoLuaError }, // 3 FileRunnerParam{ "lane/tasking_cancelling", TestType::AssertNoLuaError }, // 3 FileRunnerParam{ "lane/tasking_comms_criss_cross", TestType::AssertNoLuaError }, // 4 FileRunnerParam{ "lane/tasking_communications", TestType::AssertNoLuaError }, diff --git a/unit_tests/scripts/lane/tasking_cancelling.lua b/unit_tests/scripts/lane/tasking_cancelling.lua index 85600ab..8bee3a6 100644 --- a/unit_tests/scripts/lane/tasking_cancelling.lua +++ b/unit_tests/scripts/lane/tasking_cancelling.lua @@ -15,69 +15,12 @@ local lanes_linda = assert(lanes.linda) -- ################################################################################################## -- ################################################################################################## -local function task(a, b, c) - lane_threadname("task("..a..","..b..","..c..")") - --error "111" -- testing error messages - assert(hey) - local v=0 - for i=a,b,c do - v= v+i - end - return v, hey -end - -local gc_cb = function(name_, status_) - PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") -end - --- ################################################################################################## --- ################################################################################################## --- ################################################################################################## - -PRINT("\n\n", "---=== Tasking (cancelling) ===---", "\n\n") - -local task_launch2 = lanes_gen("", { name = 'auto', globals={hey=true}, gc_cb = gc_cb }, task) - -local N=999999999 -local lane9= task_launch2(1,N,1) -- huuuuuuge... - --- Wait until state changes "pending"->"running" --- -local st -local t0= os.time() -while os.time()-t0 < 5 do - st= lane9.status - io.stderr:write((i==1) and st.." " or '.') - if st~="pending" then break end -end -PRINT(" "..st) - -if st=="error" then - local _= lane9[0] -- propagate the error here -end -if st=="done" then - error("Looping to "..N.." was not long enough (cannot test cancellation)") -end -assert(st=="running", "st == " .. st) - --- when running under luajit, the function is JIT-ed, and the instruction count isn't hit, so we need a different hook -lane9:cancel(jit and "line" or "count", 100) -- 0 timeout, hook triggers cancelslation when reaching the specified count - -local t0= os.time() -while os.time()-t0 < 5 do - st= lane9.status - io.stderr:write((i==1) and st.." " or '.') - if st~="running" then break end -end -PRINT(" "..st) -assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'") - -- cancellation of lanes waiting on a linda local limited = lanes_linda("limited") assert.fails(function() limited:limit("key", -1) end) assert.failsnot(function() limited:limit("key", 1) end) -- [[################################################ -limited:send("key", "hello") -- saturate linda +limited:send("key", "hello") -- saturate linda, so that subsequent sends will block for k, v in pairs(limited:dump()) do PRINT("limited[" .. tostring(k) .. "] = " .. tostring(v)) end @@ -88,11 +31,15 @@ local wait_send = function() end local wait_send_lane = lanes_gen("*", { name = 'auto' }, wait_send)() -repeat until wait_send_lane.status == "waiting" -print "wait_send_lane is waiting" +repeat + io.stderr:write('!') + -- currently mingw64 builds can deadlock if we cancel the lane too early (before the linda blocks, at it causes the linda condvar not to be signalled) + lanes.sleep(0.1) +until wait_send_lane.status == "waiting" +PRINT "wait_send_lane is waiting" wait_send_lane:cancel() -- hard cancel, 0 timeout repeat until wait_send_lane.status == "cancelled" -print "wait_send_lane is cancelled" +PRINT "wait_send_lane is cancelled" --################################################]] local wait_receive = function() local k, v @@ -101,11 +48,15 @@ local wait_receive = function() end local wait_receive_lane = lanes_gen("*", { name = 'auto' }, wait_receive)() -repeat until wait_receive_lane.status == "waiting" -print "wait_receive_lane is waiting" +repeat + io.stderr:write('!') + -- currently mingw64 builds can deadlock if we cancel the lane too early (before the linda blocks, at it causes the linda condvar not to be signalled) + lanes.sleep(0.1) +until wait_receive_lane.status == "waiting" +PRINT "wait_receive_lane is waiting" wait_receive_lane:cancel() -- hard cancel, 0 timeout repeat until wait_receive_lane.status == "cancelled" -print "wait_receive_lane is cancelled" +PRINT "wait_receive_lane is cancelled" --################################################]] local wait_receive_batched = function() local k, v1, v2 @@ -114,9 +65,13 @@ local wait_receive_batched = function() end local wait_receive_batched_lane = lanes_gen("*", { name = 'auto' }, wait_receive_batched)() -repeat until wait_receive_batched_lane.status == "waiting" -print "wait_receive_batched_lane is waiting" +repeat + io.stderr:write('!') + -- currently mingw64 builds can deadlock if we cancel the lane too early (before the linda blocks, at it causes the linda condvar not to be signalled) + lanes.sleep(0.1) +until wait_receive_batched_lane.status == "waiting" +PRINT "wait_receive_batched_lane is waiting" wait_receive_batched_lane:cancel() -- hard cancel, 0 timeout repeat until wait_receive_batched_lane.status == "cancelled" -print "wait_receive_batched_lane is cancelled" +PRINT "wait_receive_batched_lane is cancelled" --################################################]] diff --git a/unit_tests/scripts/lane/tasking_cancelling_with_hook.lua b/unit_tests/scripts/lane/tasking_cancelling_with_hook.lua new file mode 100644 index 0000000..56b934f --- /dev/null +++ b/unit_tests/scripts/lane/tasking_cancelling_with_hook.lua @@ -0,0 +1,68 @@ +local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure(config).configure() +print("require_lanes_result:", require_lanes_result_1, require_lanes_result_2) +local lanes = require_lanes_result_1 + +local require_assert_result_1, require_assert_result_2 = require "_assert" +print("require_assert_result:", require_assert_result_1, require_assert_result_2) + +local utils = lanes.require "_utils" +local PRINT = utils.MAKE_PRINT() + +-- ################################################################################################## +-- ################################################################################################## +-- ################################################################################################## + +local function task(a, b, c) + lane_threadname("task("..a..","..b..","..c..")") + --error "111" -- testing error messages + assert(hey) + local v=0 + for i=a,b,c do + v= v+i + end + return v, hey +end + +local gc_cb = function(name_, status_) + PRINT(" ---> lane '" .. name_ .. "' collected with status '" .. status_ .. "'") +end + +-- ################################################################################################## +-- ################################################################################################## +-- ################################################################################################## + +local generator = lanes.gen("", { name = 'auto', globals={hey=true}, gc_cb = gc_cb }, task) + +local N = 999999999 +local lane_h = generator(1,N,1) -- huuuuuuge... + +-- Wait until state changes "pending"->"running" +-- +local st +local t0 = os.time() +while os.time()-t0 < 5 do + st = lane_h.status + io.stderr:write((i==1) and st.." " or '.') + if st~="pending" then break end +end +PRINT(" "..st) + +if st == "error" then + local _ = lane_h[0] -- propagate the error here +end +if st == "done" then + error("Looping to "..N.." was not long enough (cannot test cancellation)") +end +assert(st == "running", "st == " .. st) + +-- when running under luajit, the function is JIT-ed, and the instruction count isn't hit, so we need a different hook +lane_h:cancel(jit and "line" or "count", 100) -- 0 timeout, hook triggers cancelslation when reaching the specified count + +local t0 = os.time() +while os.time()-t0 < 5 do + st = lane_h.status + io.stderr:write((i==1) and st.." " or '.') + if st~="running" then break end +end +PRINT(" "..st) +assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'") -- cgit v1.2.3-55-g6feb