From 4c5d11823802175cfaf083a6fcd20a3006b27d51 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Fri, 18 Apr 2025 17:53:27 +0200 Subject: A unit test for linda wake_period --- src/linda.cpp | 10 +++++++- src/macros_and_utils.hpp | 1 + tests/track_lanes.lua | 6 +++-- unit_tests/UnitTests.vcxproj | 1 + unit_tests/UnitTests.vcxproj.filters | 3 +++ unit_tests/linda_tests.cpp | 17 ++++--------- unit_tests/scripts/linda/wake_period.lua | 41 ++++++++++++++++++++++++++++++++ 7 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 unit_tests/scripts/linda/wake_period.lua diff --git a/src/linda.cpp b/src/linda.cpp index a9ae61c..fa28385 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -1178,7 +1178,15 @@ LUAG_FUNC(linda) if (lua_isnil(L_, kIdxTop)) { lua_pop(L_, 1); lua_pushnumber(L_, _U->lindaWakePeriod.count()); - } else { + } else if (luaG_type(L_, kIdxTop) == LuaType::STRING) { + if (luaG_tostring(L_, kIdxTop) != "never") { + luaL_argerror(L_, 1, "invalid wake_period"); + } else { + lua_pop(L_, 1); + lua_pushnumber(L_, 0); + } + } + else { luaL_argcheck(L_, luaL_optnumber(L_, 2, 0) > 0, 1, "wake_period must be > 0"); } diff --git a/src/macros_and_utils.hpp b/src/macros_and_utils.hpp index 16011f7..1aca818 100644 --- a/src/macros_and_utils.hpp +++ b/src/macros_and_utils.hpp @@ -21,6 +21,7 @@ inline void STACK_GROW(lua_State* const L_, int const n_) // ################################################################################################# +// 1 unit of lua_Duration lasts 1 second (using default period of std::ratio<1>) using lua_Duration = std::chrono::template duration; // ################################################################################################# diff --git a/tests/track_lanes.lua b/tests/track_lanes.lua index d1670ae..ef2ca06 100644 --- a/tests/track_lanes.lua +++ b/tests/track_lanes.lua @@ -59,8 +59,10 @@ local threads = track( "============= START", 2) -- two_seconds forever assert(threads[1].status == 'waiting' and threads[2].status == 'waiting') --- wait until ephemeral1 has completed -SLEEP(2.1) +-- wait until ephemeral1 has completed, should take about 2 seconds +repeat + SLEEP(0.1) +until ephemeral1.status == "done" local threads = track( "============= two_seconds dead", 2) -- two_seconds forever diff --git a/unit_tests/UnitTests.vcxproj b/unit_tests/UnitTests.vcxproj index e1370c3..8c22653 100644 --- a/unit_tests/UnitTests.vcxproj +++ b/unit_tests/UnitTests.vcxproj @@ -968,6 +968,7 @@ + diff --git a/unit_tests/UnitTests.vcxproj.filters b/unit_tests/UnitTests.vcxproj.filters index b0ce216..ce8cb58 100644 --- a/unit_tests/UnitTests.vcxproj.filters +++ b/unit_tests/UnitTests.vcxproj.filters @@ -116,5 +116,8 @@ Scripts\lane + + Scripts\linda + \ No newline at end of file diff --git a/unit_tests/linda_tests.cpp b/unit_tests/linda_tests.cpp index 9dbaa85..d956ae2 100644 --- a/unit_tests/linda_tests.cpp +++ b/unit_tests/linda_tests.cpp @@ -104,16 +104,7 @@ TEST_CASE("linda.single_keeper.creation/wake_period") S.requireFailure("lanes.linda{wake_period = -1}"); S.requireFailure("lanes.linda{wake_period = 0}"); S.requireSuccess("lanes.linda{wake_period = 0.0001}"); -} - -// ################################################################################################# - -TEST_CASE("linda.single_keeper.wake_period") -{ - FAIL("TODO: check that wake_period works as expected"); - // - use configure default if not provided - // - overrides default when provided - // - blocking operation wakes at the specified period + S.requireSuccess("lanes.linda{wake_period = 'never'}"); } // ################################################################################################# @@ -397,17 +388,19 @@ TEST_CASE("scripted_tests." #DIR "." #FILE) \ _runner.performTest(FileRunnerParam{ #DIR "/" #FILE, TestType::AssertNoLuaError }); \ } +MAKE_TEST_CASE(linda, multiple_keepers) MAKE_TEST_CASE(linda, send_receive) MAKE_TEST_CASE(linda, send_registered_userdata) -MAKE_TEST_CASE(linda, multiple_keepers) +MAKE_TEST_CASE(linda, wake_period) /* TEST_CASE("linda.scripted_tests") { auto const& _testParam = GENERATE( + FileRunnerParam{ "linda/multiple_keepers", TestType::AssertNoLuaError }, FileRunnerParam{ "linda/send_receive", TestType::AssertNoLuaError }, FileRunnerParam{ "linda/send_registered_userdata", TestType::AssertNoLuaError }, - FileRunnerParam{ "linda/multiple_keepers", TestType::AssertNoLuaError } + FileRunnerParam{ "linda/wake_period", TestType::AssertNoLuaError } ); FileRunner _runner(R"(.\unit_tests\scripts)"); diff --git a/unit_tests/scripts/linda/wake_period.lua b/unit_tests/scripts/linda/wake_period.lua new file mode 100644 index 0000000..e4a900d --- /dev/null +++ b/unit_tests/scripts/linda/wake_period.lua @@ -0,0 +1,41 @@ +-- default wake period is 0.5 seconds +local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure{linda_wake_period = 0.5} +local lanes = require_lanes_result_1 + +-- a lane that performs a blocking operation for 2 seconds +local body = function(linda_) + -- a blocking read that lasts longer than the tested wake_period values + linda_:receive(2, "empty_slot") + return true +end + +-- if we don't cancel the lane, we should wait the whole duration +local function check_wake_duration(linda_, expected_, do_cancel_) + local h = lanes.gen(body)(linda_) + -- wait until the linda is blocked + repeat until h.status == "waiting" + local t0 = lanes.now_secs() + -- soft cancel, no timeout, no waking + if do_cancel_ then + local result, reason = h:cancel('soft', 0, false) + -- should say there was a timeout, since the lane didn't actually cancel (normal since we did not wake it) + assert(result == false and reason == 'timeout', "unexpected cancel result") + end + -- this should wait until the linda wakes by itself before the actual receive timeout and sees the cancel request + h:join() + local t1 = lanes.now_secs() + local delta = t1 - t0 + -- the linda should check for cancellation at about the expected period, not earlier + assert(delta >= expected_, tostring(linda_) .. " woke too early:" .. delta) + -- the lane shouldn't terminate too long after cancellation was processed + assert(delta <= expected_ * 1.1, tostring(linda_) .. " woke too late: " .. delta) +end + +-- legacy behavior: linda waits until operation timeout +check_wake_duration(lanes.linda{name = "A", wake_period = 'never'}, 2, true) +-- early wake behavior: linda wakes after the expected time, sees a cancellation requests, and aborts the operation early +check_wake_duration(lanes.linda{name = "B", wake_period = 0.25}, 0.25, true) +check_wake_duration(lanes.linda{name = "C"}, 0.5, true) -- wake_period defaults to 0.5 (see above) +check_wake_duration(lanes.linda{name = "D", wake_period = 1}, 1, true) +-- even when there is a wake_period, the operation should reach full timeout if not cancelled early +check_wake_duration(lanes.linda{name = "E", wake_period = 0.1}, 2, false) -- cgit v1.2.3-55-g6feb