diff --git a/src/lanes.lua b/src/lanes.lua
index bd94a14..3ee959c 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -96,6 +96,7 @@ local default_params =
-- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation
internal_allocator = isLuaJIT and "libc" or "allocator",
keepers_gc_threshold = -1,
+ linda_wake_period = 'never',
nb_user_keepers = 0,
on_state_create = nil,
shutdown_timeout = 0.25,
@@ -141,6 +142,19 @@ local param_checkers =
end
return true
end,
+ linda_wake_period = function(val_)
+ -- linda_wake_period should be a number > 0, or the string 'never'
+ if val_ == 'never' then
+ return true
+ end
+ if type(val_) ~= "number" then
+ return nil, "not a number"
+ end
+ if val_ <= 0 then
+ return nil, "value out of range"
+ end
+ return true
+ end,
nb_user_keepers = function(val_)
-- nb_user_keepers should be a number in [0,100] (so that nobody tries to run OOM by specifying a huge amount)
if type(val_) ~= "number" then
diff --git a/src/linda.cpp b/src/linda.cpp
index 0cdacfa..a9ae61c 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -237,11 +237,23 @@ namespace {
_lane->waiting_on = &_linda->writeHappened;
_lane->status.store(Lane::Waiting, std::memory_order_release);
}
+
+ // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
+ // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
+ auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] {
+ auto _until_check_cancel{ std::chrono::time_point::max() };
+ if (wakePeriod.count() > 0.0f) {
+ _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast(wakePeriod);
+ }
+ bool const _forceTryAgain{ _until_check_cancel < _until };
+ return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until);
+ });
+
// not enough data to read: wakeup when data was sent, or when timeout is reached
std::unique_lock _guard{ _keeper->mutex, std::adopt_lock };
- std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until) };
+ std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until_check_cancel) };
_guard.release(); // we don't want to unlock the mutex on exit!
- _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups
+ _try_again = _forceTryAgain || (_status == std::cv_status::no_timeout); // detect spurious wakeups
if (_lane != nullptr) {
_lane->waiting_on = nullptr;
_lane->status.store(_prev_status, std::memory_order_release);
@@ -296,9 +308,10 @@ LUAG_FUNC(linda);
// #################################################################################################
// #################################################################################################
-Linda::Linda(Universe* const U_, LindaGroup const group_, std::string_view const& name_)
+Linda::Linda(Universe* const U_, std::string_view const& name_, lua_Duration const wake_period_, LindaGroup const group_)
: DeepPrelude{ LindaFactory::Instance }
, U{ U_ }
+, wakePeriod{ wake_period_ }
, keeperIndex{ group_ % U_->keepers.getNbKeepers() }
{
setName(name_);
@@ -330,9 +343,13 @@ Linda* Linda::CreateTimerLinda(lua_State* const L_)
STACK_CHECK_START_REL(L_, 0); // L_:
// Initialize 'timerLinda'; a common Linda object shared by all states
lua_pushcfunction(L_, LG_linda); // L_: lanes.linda
- luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda "lanes-timer"
- lua_pushinteger(L_, 0); // L_: lanes.linda "lanes-timer" 0
- lua_call(L_, 2, 1); // L_: linda
+ lua_createtable(L_, 0, 3); // L_: lanes.linda {}
+ luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda {} "lanes-timer"
+ luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "name" }); // L_: lanes.linda { .name="lanes-timer" }
+ lua_pushinteger(L_, 0); // L_: lanes.linda { .name="lanes-timer" } 0
+ luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "group" }); // L_: lanes.linda { .name="lanes-timer" .group = 0 }
+ // note that wake_period is not set (will default to the value in the universe)
+ lua_call(L_, 1, 1); // L_: linda
STACK_CHECK(L_, 1);
// Proxy userdata contents is only a 'DeepPrelude*' pointer
@@ -941,11 +958,23 @@ LUAG_FUNC(linda_send)
_lane->waiting_on = &_linda->readHappened;
_lane->status.store(Lane::Waiting, std::memory_order_release);
}
+
+ // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests,
+ // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it
+ auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] {
+ auto _until_check_cancel{ std::chrono::time_point::max() };
+ if (wakePeriod.count() > 0.0f) {
+ _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast(wakePeriod);
+ }
+ bool const _forceTryAgain{ _until_check_cancel < _until };
+ return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until);
+ });
+
// could not send because no room: wait until some data was read before trying again, or until timeout is reached
std::unique_lock _guard{ _keeper->mutex, std::adopt_lock };
- std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until) };
+ std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until_check_cancel) };
_guard.release(); // we don't want to unlock the mutex on exit!
- _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups
+ _try_again = _forceTryAgain || (status == std::cv_status::no_timeout); // detect spurious wakeups
if (_lane != nullptr) {
_lane->waiting_on = nullptr;
_lane->status.store(_prev_status, std::memory_order_release);
@@ -1129,88 +1158,69 @@ namespace {
// #################################################################################################
/*
- * ud = lanes.linda( [name[,group[,close_handler]]])
+ * ud = lanes.linda{.name = , .group = , .close_handler = , .wake_period = }
*
* returns a linda object, or raises an error if creation failed
*/
LUAG_FUNC(linda)
{
- static constexpr StackIndex kLastArg{ LUA_VERSION_NUM >= 504 ? 3 : 2 };
+ // unpack the received table on the stack, putting name wake_period group close_handler in that order
StackIndex const _top{ lua_gettop(L_) };
- luaL_argcheck(L_, _top <= kLastArg, _top, "too many arguments");
- StackIndex _closeHandlerIdx{};
- StackIndex _nameIdx{};
- StackIndex _groupIdx{};
- for (StackIndex const _i : std::ranges::iota_view{ StackIndex{ 1 }, StackIndex{ _top + 1 }}) {
- switch (luaG_type(L_, _i)) {
+ luaL_argcheck(L_, _top <= 1, _top, "too many arguments");
+ if (_top == 0) {
+ lua_settop(L_, 3); // L_: nil nil nil
+ }
+ else if (!lua_istable(L_, kIdxTop)) {
+ luaL_argerror(L_, 1, "expecting a table");
+ } else {
+ auto* const _U{ Universe::Get(L_) };
+ lua_getfield(L_, 1, "wake_period"); // L_: {} wake_period
+ if (lua_isnil(L_, kIdxTop)) {
+ lua_pop(L_, 1);
+ lua_pushnumber(L_, _U->lindaWakePeriod.count());
+ } else {
+ luaL_argcheck(L_, luaL_optnumber(L_, 2, 0) > 0, 1, "wake_period must be > 0");
+ }
+
+ lua_getfield(L_, 1, "group"); // L_: {} wake_period group
+ int const _nbKeepers{ _U->keepers.getNbKeepers() };
+ if (lua_isnil(L_, kIdxTop)) {
+ luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios");
+ } else {
+ int const _group{ static_cast(lua_tointeger(L_, kIdxTop)) };
+ luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, 1, "group out of range");
+ }
+
#if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4
- case LuaType::FUNCTION:
- luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler");
- _closeHandlerIdx = _i;
- break;
-
- case LuaType::USERDATA:
- case LuaType::TABLE:
- luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler");
- luaL_argcheck(L_, luaL_getmetafield(L_, _i, "__call") != 0, _i, "__close handler is not callable");
+ lua_getfield(L_, 1, "close_handler"); // L_: {} wake_period group close_handler
+ LuaType const _handlerType{ luaG_type(L_, kIdxTop) };
+ if (_handlerType == LuaType::NIL) {
+ lua_pop(L_, 1); // L_: {} wake_period group
+ } else if (_handlerType == LuaType::USERDATA || _handlerType == LuaType::TABLE) {
+ luaL_argcheck(L_, luaL_getmetafield(L_, kIdxTop, "__call") != 0, 1, "__close handler is not callable");
lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it
- _closeHandlerIdx = _i;
- break;
+ } else {
+ luaL_argcheck(L_, _handlerType == LuaType::FUNCTION, 1, "__close handler is not a function");
+ }
#endif // LUA_VERSION_NUM >= 504
- case LuaType::STRING:
- luaL_argcheck(L_, _nameIdx == 0, _i, "More than one name");
- _nameIdx = _i;
- break;
-
- case LuaType::NUMBER:
- luaL_argcheck(L_, _groupIdx == 0, _i, "More than one group");
- _groupIdx = _i;
- break;
-
- default:
- luaL_argcheck(L_, false, _i, "Bad argument type (should be a string, a number, or a callable type)");
- }
- }
-
- int const _nbKeepers{ Universe::Get(L_)->keepers.getNbKeepers() };
- if (!_groupIdx) {
- luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios");
- } else {
- int const _group{ static_cast(lua_tointeger(L_, _groupIdx)) };
- luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, _groupIdx, "Group out of range");
+ auto const _nameType{ luaG_getfield(L_, StackIndex{ 1 }, "name") }; // L_: {} wake_period group [close_handler] name
+ luaL_argcheck(L_, _nameType == LuaType::NIL || _nameType == LuaType::STRING, 1, "name is not a string");
+ lua_replace(L_, 1); // L_: name wake_period group [close_handler]
}
// done with argument checking, let's proceed
- if constexpr (LUA_VERSION_NUM >= 504) {
- // make sure we have kMaxArgs arguments on the stack for processing, with name, group, and handler, in that order
- lua_settop(L_, kLastArg); // L_: a b c
- // If either index is 0, lua_settop() adjusted the stack with a nil in slot kLastArg
- lua_pushvalue(L_, _closeHandlerIdx ? _closeHandlerIdx : kLastArg); // L_: a b c close_handler
- lua_pushvalue(L_, _groupIdx ? _groupIdx : kLastArg); // L_: a b c close_handler group
- lua_pushvalue(L_, _nameIdx ? _nameIdx : kLastArg); // L_: a b c close_handler group name
- lua_replace(L_, 1); // L_: name b c close_handler group
- lua_replace(L_, 2); // L_: name group c close_handler
- lua_replace(L_, 3); // L_: name group close_handler
-
+ if (lua_gettop(L_) == 4) {
// if we have a __close handler, we need a uservalue slot to store it
- UserValueCount const _nuv{ _closeHandlerIdx ? 1 : 0 };
- LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, _nuv); // L_: name group close_handler linda
- if (_closeHandlerIdx != 0) {
- lua_replace(L_, 2); // L_: name linda close_handler
- lua_setiuservalue(L_, StackIndex{ 2 }, UserValueIndex{ 1 }); // L_: name linda
- }
+ LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 1 }); // L_: name wake_period group [close_handler] linda
+ lua_replace(L_, 3); // L_: name wake_period linda close_handler
+ lua_setiuservalue(L_, StackIndex{ 3 }, UserValueIndex{ 1 }); // L_: name wake_period linda
// depending on whether we have a handler or not, the stack is not in the same state at this point
// just make sure we have our Linda at the top
LUA_ASSERT(L_, ToLinda(L_, kIdxTop));
return 1;
} else { // no to-be-closed support
- // ensure we have name, group in that order on the stack
- if (_nameIdx > _groupIdx) {
- lua_insert(L_, 1); // L_: name group
- }
- LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name group linda
+ LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name wake_period group linda
return 1;
}
-
}
diff --git a/src/linda.hpp b/src/linda.hpp
index 2d5c9dc..7874db3 100644
--- a/src/linda.hpp
+++ b/src/linda.hpp
@@ -42,19 +42,21 @@ class Linda final
};
using enum Status;
- private:
+ public:
+ Universe* const U{ nullptr }; // the universe this linda belongs to
+ private:
static constexpr size_t kEmbeddedNameLength = 24;
using EmbeddedName = std::array;
// depending on the name length, it is either embedded inside the Linda, or allocated separately
std::variant nameVariant{};
// counts the keeper operations in progress
std::atomic keeperOperationCount{};
+ lua_Duration wakePeriod{};
public:
std::condition_variable readHappened{};
std::condition_variable writeHappened{};
- Universe* const U{ nullptr }; // the universe this linda belongs to
KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda
Status cancelStatus{ Status::Active };
@@ -68,7 +70,7 @@ class Linda final
static void operator delete(void* p_) { static_cast(p_)->U->internalAllocator.free(p_, sizeof(Linda)); }
~Linda();
- Linda(Universe* U_, LindaGroup group_, std::string_view const& name_);
+ Linda(Universe* U_, std::string_view const& name_, lua_Duration wake_period_, LindaGroup group_);
Linda() = delete;
// non-copyable, non-movable
Linda(Linda const&) = delete;
@@ -92,6 +94,8 @@ class Linda final
[[nodiscard]]
std::string_view getName() const;
[[nodiscard]]
+ auto getWakePeriod() const { return wakePeriod; }
+ [[nodiscard]]
bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; }
template
[[nodiscard]]
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp
index 42d0984..4eab0c1 100644
--- a/src/lindafactory.cpp
+++ b/src/lindafactory.cpp
@@ -108,9 +108,11 @@ std::string_view LindaFactory::moduleName() const
DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const
{
- // we always expect name and group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified
+ STACK_CHECK_START_REL(L_, 0);
+ // we always expect name, wake_period, group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified
std::string_view _linda_name{ luaG_tostring(L_, StackIndex{ 1 }) };
- LindaGroup _linda_group{ static_cast(lua_tointeger(L_, 2)) };
+ auto const _wake_period{ static_cast(lua_tonumber(L_, 2)) };
+ LindaGroup const _linda_group{ static_cast(lua_tointeger(L_, 3)) };
// store in the linda the location of the script that created it
if (_linda_name == "auto") {
@@ -129,6 +131,7 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const
// The deep data is allocated separately of Lua stack; we might no longer be around when last reference to it is being released.
// One can use any memory allocation scheme. Just don't use L's allocF because we don't know which state will get the honor of GCing the linda
Universe* const _U{ Universe::Get(L_) };
- Linda* const _linda{ new (_U) Linda{ _U, _linda_group, _linda_name } };
+ Linda* const _linda{ new (_U) Linda{ _U, _linda_name, _wake_period, _linda_group } };
+ STACK_CHECK(L_, 0);
return _linda;
}
diff --git a/src/universe.cpp b/src/universe.cpp
index 89ad02a..335f056 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -153,6 +153,14 @@ Universe* Universe::Create(lua_State* const L_)
lua_setmetatable(L_, -2); // L_: settings universe
lua_pop(L_, 1); // L_: settings
+ std::ignore = luaG_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period
+ if (luaG_type(L_, kIdxTop) == LuaType::NUMBER) {
+ _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) };
+ } else {
+ LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "never");
+ }
+ lua_pop(L_, 1); // L_: settings
+
std::ignore = luaG_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions
_U->stripFunctions = lua_toboolean(L_, -1) ? true : false;
lua_pop(L_, 1); // L_: settings
diff --git a/src/universe.hpp b/src/universe.hpp
index 42a3d83..0c5e659 100644
--- a/src/universe.hpp
+++ b/src/universe.hpp
@@ -99,6 +99,8 @@ class Universe final
Keepers keepers;
+ lua_Duration lindaWakePeriod{};
+
// Initialized by 'init_once_LOCKED()': the deep userdata Linda object
// used for timers (each lane will get a proxy to this)
Linda* timerLinda{ nullptr };
diff --git a/tests/basic.lua b/tests/basic.lua
index a9c85cc..9aaad97 100644
--- a/tests/basic.lua
+++ b/tests/basic.lua
@@ -163,7 +163,7 @@ PRINT(" "..st)
assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'")
-- cancellation of lanes waiting on a linda
-local limited = lanes_linda("limited")
+local limited = lanes_linda{name = "limited"}
assert.fails(function() limited:limit("key", -1) end)
assert.failsnot(function() limited:limit("key", 1) end)
-- [[################################################
@@ -255,7 +255,7 @@ local chunk= function(linda)
WR("chunk ", "Lane ends!\n")
end
-local linda = lanes_linda("communications")
+local linda = lanes_linda{name = "communications"}
assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications")
--
-- ["->"] master -> slave
@@ -410,7 +410,7 @@ local tc = lanes.gen("io", { name = 'auto', gc_cb = gc_cb },
end
)
-local linda= lanes_linda("criss cross")
+local linda= lanes_linda{name = "criss cross"}
local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms
@@ -461,7 +461,7 @@ local function chunk2(linda)
linda:send("up", function() return ":)" end, "ok2")
end
-local linda = lanes_linda("auto")
+local linda = lanes_linda{name = "auto"}
local t2 = lanes.gen("debug,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch
linda:send("down", function(linda) linda:send("up", "ready!") end,
"ok")
diff --git a/tests/cancel.lua b/tests/cancel.lua
index 80e6c6a..66957c3 100644
--- a/tests/cancel.lua
+++ b/tests/cancel.lua
@@ -148,7 +148,7 @@ local protectedBody = function(...)
local paramLessClosure = function() laneBody(unpack(params)) end
local status, message = xpcall(paramLessClosure, errorHandler)
if status == false then
- print(" error handler rethrowing '" .. (ce == message and "cancel_error"or tostring(message)) .. "'")
+ print(" protectedBody rethrowing '" .. (ce == message and "cancel_error" or tostring(message)) .. "'")
-- if the error isn't rethrown, the lane's finalizer won't get it
error(message)
end
diff --git a/tests/deadlock.lua b/tests/deadlock.lua
index d028e83..9b93e3b 100644
--- a/tests/deadlock.lua
+++ b/tests/deadlock.lua
@@ -16,7 +16,7 @@ print "let's begin"
local do_extra_stuff = true
if do_extra_stuff then
- local linda = lanes.linda "deadlock_linda"
+ local linda = lanes.linda{name = "deadlock_linda"}
-- just something to make send() succeed and receive() fail
local payload = { io.flush }
diff --git a/tests/error.lua b/tests/error.lua
index 306c51d..28cfff1 100644
--- a/tests/error.lua
+++ b/tests/error.lua
@@ -106,11 +106,11 @@ end
local lane_error_as_string = "'lane error as string'"
local lane_error_as_table = setmetatable({"lane error as table"}, make_table_error_mt())
-local lane_error_as_linda = lanes.linda("'lane error'")
+local lane_error_as_linda = lanes.linda{name = "'lane error'"}
local finalizer_error_as_string = "'finalizer error as string'"
local finalizer_error_as_table = setmetatable({"finalizer error as table"}, make_table_error_mt())
-local finalizer_error_as_linda = lanes.linda("'finalizer error'")
+local finalizer_error_as_linda = lanes.linda{name = "'finalizer error'"}
local test_settings = {}
local configure_tests = function()
diff --git a/tests/fifo.lua b/tests/fifo.lua
index 9efcbd9..1317a9f 100644
--- a/tests/fifo.lua
+++ b/tests/fifo.lua
@@ -6,10 +6,10 @@
local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true}
-local atomic_linda = lanes.linda( "atom")
+local atomic_linda = lanes.linda{name = "atom"}
local atomic_inc= lanes.genatomic( atomic_linda, "FIFO_n")
-local fifo_linda = lanes.linda( "fifo")
+local fifo_linda = lanes.linda{name = "fifo"}
-- Lua 5.1 support
local table_unpack = table.unpack or unpack
diff --git a/tests/keeper.lua b/tests/keeper.lua
index f566927..4742732 100644
--- a/tests/keeper.lua
+++ b/tests/keeper.lua
@@ -40,14 +40,14 @@ if true then
end
-- should succeed
- assert.failsnot(function() createLinda("zero", 0) end)
- assert.failsnot(function() createLinda("one", 1) end)
- assert.failsnot(function() createLinda("two", 2) end)
- assert.failsnot(function() createLinda("three", 3) end)
+ assert.failsnot(function() createLinda{name = "zero", group = 0} end)
+ assert.failsnot(function() createLinda{name = "one", group = 1} end)
+ assert.failsnot(function() createLinda{name = "two", group = 2} end)
+ assert.failsnot(function() createLinda{name = "three", group = 3} end)
-- should fail (and not create the lindas)
- assert.fails(function() createLinda("minus 1", -1) end)
- assert.fails(function() createLinda("none") end)
- assert.fails(function() createLinda("four", 4) end)
+ assert.fails(function() createLinda{name = "minus 1", group = -1} end)
+ assert.fails(function() createLinda{name = "none"} end)
+ assert.fails(function() createLinda{name = "four", group = 4} end)
end
-- should only collect the 4 successfully created lindas
@@ -58,11 +58,11 @@ DONE()
if true then
PRINT "========================================================================================="
PRINT "Linda names test:"
- local unnamedLinda1 = lanes.linda(1)
- local unnamedLinda2 = lanes.linda("", 2)
- local veeeerrrryyyylooongNamedLinda3 = lanes.linda( "veeeerrrryyyylooongNamedLinda", 3)
+ local unnamedLinda1 = lanes.linda{group = 1}
+ local unnamedLinda2 = lanes.linda{name = "", group = 2}
+ local veeeerrrryyyylooongNamedLinda3 = lanes.linda{ name = "veeeerrrryyyylooongNamedLinda", group = 3}
assert(tostring(veeeerrrryyyylooongNamedLinda3) == "Linda: veeeerrrryyyylooongNamedLinda")
- local shortNamedLinda0 = lanes.linda( "short", 0)
+ local shortNamedLinda0 = lanes.linda{name = "short", group = 0}
assert(tostring(shortNamedLinda0) == "Linda: short")
PRINT(shortNamedLinda0, unnamedLinda1, unnamedLinda2, veeeerrrryyyylooongNamedLinda3)
end
@@ -74,12 +74,12 @@ DONE()
if true then
PRINT "========================================================================================="
PRINT "Linda GC test:"
- local a = lanes.linda("A", 1)
- local b = lanes.linda("B", 2)
- local c = lanes.linda("C", 3)
+ local a = lanes.linda{name = "A", group = 1}
+ local b = lanes.linda{name = "B", group = 2}
+ local c = lanes.linda{name = "C", group = 3}
-- store lindas in each other and in themselves
- a:set("here", lanes.linda("temporary linda", 0))
+ a:set("here", lanes.linda{name = "temporary linda", group = 0})
b:set("here", a, b, c)
c:set("here", a, b, c)
@@ -120,13 +120,13 @@ if true then
end
--
- local lindaA= lanes.linda( "A", 1)
+ local lindaA= lanes.linda{name = "A", group = 1}
local A= keeper( lindaA )
- local lindaB= lanes.linda( "B", 2)
+ local lindaB= lanes.linda{name = "B", group = 2}
local B= keeper( lindaB )
- local lindaC= lanes.linda( "C", 3)
+ local lindaC= lanes.linda{name = "C", group = 3}
local C= keeper( lindaC )
PRINT("Created", lindaA, lindaB, lindaC)
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua
index 107fd25..83b8921 100644
--- a/tests/linda_perf.lua
+++ b/tests/linda_perf.lua
@@ -22,7 +22,7 @@ if true then
do
print "############################################ tests get/set"
-- linda:get throughput
- local l = lanes.linda("get/set", 1)
+ local l = lanes.linda{name = "get/set", group = 1}
local batch = {}
for i = 1,1000 do
table.insert(batch, i)
@@ -90,7 +90,7 @@ local group_uid = 1
local function ziva1( preloop, loop, batch)
-- prefill the linda a bit to increase fifo stress
local top = math.max( preloop, loop)
- local l = lanes.linda("ziva1("..preloop..":"..loop..":"..batch..")", group_uid)
+ local l = lanes.linda{name = "ziva1("..preloop..":"..loop..":"..batch..")", group = group_uid}
group_uid = (group_uid % config.nb_user_keepers) + 1
local t1 = lanes.now_secs()
for i = 1, preloop do
@@ -165,7 +165,7 @@ end
-- sequential write/read (no parallelization involved)
local function ziva2( preloop, loop, batch)
- local l = lanes.linda("ziva2("..preloop..":"..loop..":"..tostring(batch)..")", group_uid)
+ local l = lanes.linda{name = "ziva2("..preloop..":"..loop..":"..tostring(batch)..")", group = group_uid}
group_uid = (group_uid % config.nb_user_keepers) + 1
-- prefill the linda a bit to increase fifo stress
local top, step = math.max( preloop, loop), batch or 1
diff --git a/tests/tobeclosed.lua b/tests/tobeclosed.lua
index ef09df3..447b936 100644
--- a/tests/tobeclosed.lua
+++ b/tests/tobeclosed.lua
@@ -36,7 +36,7 @@ do
WR("f closing ", linda_)
closed_by_f = true
end
- local lf = lanes.linda("closed by f", close_handler_f)
+ local lf = lanes.linda{name = "closed by f", close_handler = close_handler_f}
local close_handler_t = setmetatable({},
{
@@ -46,7 +46,7 @@ do
end
}
)
- local lt = lanes.linda("closed by t", close_handler_t)
+ local lt = lanes.linda{name = "closed by t", close_handler = close_handler_t}
end
assert(closed_by_f == true)
assert(closed_by_t == true)
@@ -58,13 +58,13 @@ end
WR "================================================================================================"
WR "Through Linda"
do
- local l = lanes.linda("channel")
+ local l = lanes.linda{name = "channel"}
local close_handler_f = function(linda_, err_)
WR("f closing ", linda_)
linda_:set("closed", true)
end
- local l_in = lanes.linda("voyager", close_handler_f)
+ local l_in = lanes.linda{name = "voyager", close_handler = close_handler_f}
l:set("trip", l_in)
do
@@ -99,7 +99,7 @@ end
WR "================================================================================================"
WR "Linda closing through Lane"
do
- local l = lanes.linda("channel")
+ local l = lanes.linda{name = "channel"}
local lane_body = function(l_arg_)
WR "In lane body"
-- linda obtained through a linda
@@ -114,7 +114,7 @@ do
local _count, _closed = linda_:get("closed")
linda_:set("closed", (_closed or 0) + 1)
end
- local l_in = lanes.linda("voyager", close_handler_f)
+ local l_in = lanes.linda{name = "voyager", close_handler = close_handler_f}
l:set("trip", l_in)
do
diff --git a/unit_tests/embedded_tests.cpp b/unit_tests/embedded_tests.cpp
index 0991a4c..a0a7bb2 100644
--- a/unit_tests/embedded_tests.cpp
+++ b/unit_tests/embedded_tests.cpp
@@ -157,7 +157,7 @@ TEST_CASE("lanes.embedding.with default allocator")
// function with an upvalue
std::string_view const _script{
" local lanes = require 'lanes'.configure{with_timers = false}"
- " local l = lanes.linda'gleh'"
+ " local l = lanes.linda{name = 'gleh'}"
" local upvalue = 'oeauaoeuoeuaoeuaoeujaoefubycfjbycfybcfjybcfjybcfjbcf'"
" local upvalued = function()"
" return upvalue"
@@ -183,7 +183,7 @@ TEST_CASE("lanes.embedding.with default allocator")
// try to send io.open into a linda, which fails if io base library is not loaded
std::string_view const _script{
" local lanes = require 'lanes'"
- " local l = lanes.linda'gleh'"
+ " local l = lanes.linda{name = 'gleh'}"
" l:set('yo', io.open)"
" return 'SUCCESS'"
};
diff --git a/unit_tests/init_and_shutdown.cpp b/unit_tests/init_and_shutdown.cpp
index 384af43..bd72157 100644
--- a/unit_tests/init_and_shutdown.cpp
+++ b/unit_tests/init_and_shutdown.cpp
@@ -289,6 +289,62 @@ TEST_CASE("lanes.configure.keepers_gc_threshold")
// #################################################################################################
+TEST_CASE("lanes.configure.linda_wake_period")
+{
+ LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
+
+ // linda_wake_period should be a number > 0, or 'never'
+
+ SECTION("linda_wake_period = ")
+ {
+ L.requireFailure("require 'lanes'.configure{linda_wake_period = {}}");
+ }
+
+ // ---------------------------------------------------------------------------------------------
+
+ SECTION("linda_wake_period = ")
+ {
+ L.requireFailure("require 'lanes'.configure{linda_wake_period = 'gluh'}");
+ }
+
+ // ---------------------------------------------------------------------------------------------
+
+ SECTION("linda_wake_period = 'never'")
+ {
+ L.requireSuccess("require 'lanes'.configure{linda_wake_period = 'never'}");
+ }
+
+ // ---------------------------------------------------------------------------------------------
+
+ SECTION("linda_wake_period = ")
+ {
+ L.requireFailure("require 'lanes'.configure{linda_wake_period = -0.001}");
+ }
+
+ // ---------------------------------------------------------------------------------------------
+
+ SECTION("linda_wake_period = 0")
+ {
+ L.requireFailure("require 'lanes'.configure{linda_wake_period = 0}");
+ }
+
+ // ---------------------------------------------------------------------------------------------
+
+ SECTION("linda_wake_period = 0.0001s")
+ {
+ L.requireSuccess("require 'lanes'.configure{linda_wake_period = 0.0001}");
+ }
+
+ // ---------------------------------------------------------------------------------------------
+
+ SECTION("linda_wake_period = 1e30")
+ {
+ L.requireSuccess("require 'lanes'.configure{linda_wake_period = 1e30}");
+ }
+}
+
+// #################################################################################################
+
TEST_CASE("lanes.configure.nb_user_keepers")
{
LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
@@ -300,35 +356,35 @@ TEST_CASE("lanes.configure.nb_user_keepers")
L.requireFailure("require 'lanes'.configure{nb_user_keepers = {}}");
}
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
SECTION("nb_user_keepers = ")
{
L.requireFailure("require 'lanes'.configure{nb_user_keepers = 'gluh'}");
}
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
SECTION("nb_user_keepers = -1")
{
L.requireFailure("require 'lanes'.configure{nb_user_keepers = -1}");
}
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
SECTION("nb_user_keepers = 0")
{
L.requireSuccess("require 'lanes'.configure{nb_user_keepers = 0}");
}
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
SECTION("nb_user_keepers = 1")
{
L.requireSuccess("require 'lanes'.configure{nb_user_keepers = 1}");
}
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
SECTION("nb_user_keepers = 100")
{
@@ -345,340 +401,355 @@ TEST_CASE("lanes.configure.nb_user_keepers")
// #################################################################################################
-TEST_CASE("lanes.configure.the rest")
+TEST_CASE("lanes.configure.on_state_create")
{
LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
// on_state_create should be a function, either C or Lua, without upvalues
- SECTION("on_state_create")
+ SECTION("on_state_create = ")
{
- SECTION("on_state_create = ")
- {
- L.requireFailure("require 'lanes'.configure{on_state_create = {}}");
- }
+ L.requireFailure("require 'lanes'.configure{on_state_create = {}}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("on_state_create = ")
- {
- L.requireFailure("require 'lanes'.configure{on_state_create = 'gluh'}");
- }
+ SECTION("on_state_create = ")
+ {
+ L.requireFailure("require 'lanes'.configure{on_state_create = 'gluh'}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("on_state_create = ")
- {
- L.requireFailure("require 'lanes'.configure{on_state_create = 1}");
- }
+ SECTION("on_state_create = ")
+ {
+ L.requireFailure("require 'lanes'.configure{on_state_create = 1}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("on_state_create = false")
- {
- L.requireFailure("require 'lanes'.configure{on_state_create = false}");
- }
+ SECTION("on_state_create = false")
+ {
+ L.requireFailure("require 'lanes'.configure{on_state_create = false}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("on_state_create = true")
- {
- L.requireFailure("require 'lanes'.configure{on_state_create = true}");
- }
+ SECTION("on_state_create = true")
+ {
+ L.requireFailure("require 'lanes'.configure{on_state_create = true}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("on_state_create = ")
- {
- // on_state_create isn't called inside a Keeper state if it's a Lua function (which is good as print() doesn't exist there!)
- L.requireSuccess("local print = print; require 'lanes'.configure{on_state_create = function() print 'hello' end}");
- }
+ SECTION("on_state_create = ")
+ {
+ // on_state_create isn't called inside a Keeper state if it's a Lua function (which is good as print() doesn't exist there!)
+ L.requireSuccess("local print = print; require 'lanes'.configure{on_state_create = function() print 'hello' end}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("on_state_create = ")
- {
- // funnily enough, in Lua 5.3, print() uses global tostring(), that doesn't exist in a keeper since we didn't open libs -> "attempt to call a nil value"
- // conclusion, don't use print() as a fake on_state_create() callback!
- // assert() should be fine since we pass a non-false argument to on_state_create
- L.requireSuccess("require 'lanes'.configure{on_state_create = assert}");
- }
+ SECTION("on_state_create = ")
+ {
+ // funnily enough, in Lua 5.3, print() uses global tostring(), that doesn't exist in a keeper since we didn't open libs -> "attempt to call a nil value"
+ // conclusion, don't use print() as a fake on_state_create() callback!
+ // assert() should be fine since we pass a non-false argument to on_state_create
+ L.requireSuccess("require 'lanes'.configure{on_state_create = assert}");
}
+}
+
+// #################################################################################################
+
+TEST_CASE("lanes.configure.shutdown_timeout")
+{
+ LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
- // ---------------------------------------------------------------------------------------------
// shutdown_timeout should be a number in [0,3600]
- SECTION("shutdown_timeout")
+ SECTION("shutdown_timeout = ")
{
- SECTION("shutdown_timeout = ")
- {
- L.requireFailure("require 'lanes'.configure{shutdown_timeout = {}}");
- }
+ L.requireFailure("require 'lanes'.configure{shutdown_timeout = {}}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("shutdown_timeout = ")
- {
- L.requireFailure("require 'lanes'.configure{shutdown_timeout = 'gluh'}");
- }
+ SECTION("shutdown_timeout = ")
+ {
+ L.requireFailure("require 'lanes'.configure{shutdown_timeout = 'gluh'}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("shutdown_timeout = ")
- {
- L.requireFailure("require 'lanes'.configure{shutdown_timeout = -0.001}");
- }
+ SECTION("shutdown_timeout = ")
+ {
+ L.requireFailure("require 'lanes'.configure{shutdown_timeout = -0.001}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("shutdown_timeout = 0")
- {
- L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 0}");
- }
+ SECTION("shutdown_timeout = 0")
+ {
+ L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 0}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("shutdown_timeout = 1s")
- {
- L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 1}");
- }
+ SECTION("shutdown_timeout = 1s")
+ {
+ L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 1}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("shutdown_timeout = 3600s")
- {
- L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 3600}");
- }
+ SECTION("shutdown_timeout = 3600s")
+ {
+ L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 3600}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("shutdown_timeout = ")
- {
- L.requireFailure("require 'lanes'.configure{shutdown_timeout = 3600.001}");
- }
+ SECTION("shutdown_timeout = ")
+ {
+ L.requireFailure("require 'lanes'.configure{shutdown_timeout = 3600.001}");
}
+}
+
+// #################################################################################################
+
+TEST_CASE("lanes.configure.strip_functions")
+{
+ LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
- // ---------------------------------------------------------------------------------------------
// strip_functions should be a boolean
- SECTION("strip_functions")
+ SECTION("strip_functions = ")
{
- SECTION("strip_functions = ")
- {
- L.requireFailure("require 'lanes'.configure{strip_functions = {}}");
- }
+ L.requireFailure("require 'lanes'.configure{strip_functions = {}}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("strip_functions = ")
- {
- L.requireFailure("require 'lanes'.configure{strip_functions = 'gluh'}");
- }
+ SECTION("strip_functions = ")
+ {
+ L.requireFailure("require 'lanes'.configure{strip_functions = 'gluh'}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("strip_functions = ")
- {
- L.requireFailure("require 'lanes'.configure{strip_functions = 1}");
- }
+ SECTION("strip_functions = ")
+ {
+ L.requireFailure("require 'lanes'.configure{strip_functions = 1}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("strip_functions = ")
- {
- L.requireFailure("require 'lanes'.configure{strip_functions = print}");
- }
+ SECTION("strip_functions = ")
+ {
+ L.requireFailure("require 'lanes'.configure{strip_functions = print}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("strip_functions = false")
- {
- L.requireSuccess("require 'lanes'.configure{strip_functions = false}");
- }
+ SECTION("strip_functions = false")
+ {
+ L.requireSuccess("require 'lanes'.configure{strip_functions = false}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("strip_functions = true")
- {
- L.requireSuccess("require 'lanes'.configure{strip_functions = true}");
- }
+ SECTION("strip_functions = true")
+ {
+ L.requireSuccess("require 'lanes'.configure{strip_functions = true}");
}
+}
+
+// #################################################################################################
+
+TEST_CASE("lanes.configure.track_lanes")
+{
+ LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
- // ---------------------------------------------------------------------------------------------
// track_lanes should be a boolean
- SECTION("track_lanes")
+ SECTION("track_lanes = ")
{
- SECTION("track_lanes = ")
- {
- L.requireFailure("require 'lanes'.configure{track_lanes = {}}");
- }
+ L.requireFailure("require 'lanes'.configure{track_lanes = {}}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("track_lanes = ")
- {
- L.requireFailure("require 'lanes'.configure{track_lanes = 'gluh'}");
- }
+ SECTION("track_lanes = ")
+ {
+ L.requireFailure("require 'lanes'.configure{track_lanes = 'gluh'}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("track_lanes = ")
- {
- L.requireFailure("require 'lanes'.configure{track_lanes = 1}");
- }
+ SECTION("track_lanes = ")
+ {
+ L.requireFailure("require 'lanes'.configure{track_lanes = 1}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("track_lanes = ")
- {
- L.requireFailure("require 'lanes'.configure{track_lanes = print}");
- }
+ SECTION("track_lanes = ")
+ {
+ L.requireFailure("require 'lanes'.configure{track_lanes = print}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("track_lanes = false")
- {
- L.requireSuccess("require 'lanes'.configure{track_lanes = false}");
- }
+ SECTION("track_lanes = false")
+ {
+ L.requireSuccess("require 'lanes'.configure{track_lanes = false}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("track_lanes = true")
- {
- L.requireSuccess("require 'lanes'.configure{track_lanes = true}");
- }
+ SECTION("track_lanes = true")
+ {
+ L.requireSuccess("require 'lanes'.configure{track_lanes = true}");
}
+}
+
+// #################################################################################################
+
+TEST_CASE("lanes.configure.verbose_errors")
+{
+ LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
- // ---------------------------------------------------------------------------------------------
// verbose_errors should be a boolean
- SECTION("verbose_errors")
+ SECTION("verbose_errors = ")
{
- SECTION("verbose_errors = ")
- {
- L.requireFailure("require 'lanes'.configure{verbose_errors = {}}");
- }
+ L.requireFailure("require 'lanes'.configure{verbose_errors = {}}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("verbose_errors = ")
- {
- L.requireFailure("require 'lanes'.configure{verbose_errors = 'gluh'}");
- }
+ SECTION("verbose_errors = ")
+ {
+ L.requireFailure("require 'lanes'.configure{verbose_errors = 'gluh'}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("verbose_errors = ")
- {
- L.requireFailure("require 'lanes'.configure{verbose_errors = 1}");
- }
+ SECTION("verbose_errors = ")
+ {
+ L.requireFailure("require 'lanes'.configure{verbose_errors = 1}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("verbose_errors = ")
- {
- L.requireFailure("require 'lanes'.configure{verbose_errors = print}");
- }
+ SECTION("verbose_errors = ")
+ {
+ L.requireFailure("require 'lanes'.configure{verbose_errors = print}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("verbose_errors = false")
- {
- L.requireSuccess("require 'lanes'.configure{verbose_errors = false}");
- }
+ SECTION("verbose_errors = false")
+ {
+ L.requireSuccess("require 'lanes'.configure{verbose_errors = false}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("verbose_errors = true")
- {
- L.requireSuccess("require 'lanes'.configure{verbose_errors = true}");
- }
+ SECTION("verbose_errors = true")
+ {
+ L.requireSuccess("require 'lanes'.configure{verbose_errors = true}");
}
+}
+
+// #################################################################################################
+
+TEST_CASE("lanes.configure.with_timers")
+{
+ LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
- // ---------------------------------------------------------------------------------------------
// with_timers should be a boolean
- SECTION("with_timers")
+ SECTION("with_timers = ")
{
- SECTION("with_timers = ")
- {
- L.requireFailure("require 'lanes'.configure{with_timers = {}}");
- }
+ L.requireFailure("require 'lanes'.configure{with_timers = {}}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("with_timers = ")
- {
- L.requireFailure("require 'lanes'.configure{with_timers = 'gluh'}");
- }
+ SECTION("with_timers = ")
+ {
+ L.requireFailure("require 'lanes'.configure{with_timers = 'gluh'}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("with_timers = ")
- {
- L.requireFailure("require 'lanes'.configure{with_timers = 1}");
- }
+ SECTION("with_timers = ")
+ {
+ L.requireFailure("require 'lanes'.configure{with_timers = 1}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("with_timers = ")
- {
- L.requireFailure("require 'lanes'.configure{with_timers = print}");
- }
+ SECTION("with_timers = ")
+ {
+ L.requireFailure("require 'lanes'.configure{with_timers = print}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("with_timers = false")
- {
- L.requireSuccess("require 'lanes'.configure{with_timers = false}");
- }
+ SECTION("with_timers = false")
+ {
+ L.requireSuccess("require 'lanes'.configure{with_timers = false}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("with_timers = true")
- {
- L.requireSuccess("require 'lanes'.configure{with_timers = true}");
- }
+ SECTION("with_timers = true")
+ {
+ L.requireSuccess("require 'lanes'.configure{with_timers = true}");
}
+}
+
+// #################################################################################################
+
+TEST_CASE("lanes.configure.unknown_setting")
+{
+ LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } };
- // ---------------------------------------------------------------------------------------------
// any unknown setting should be rejected
- SECTION("unknown_setting")
+ SECTION("table setting")
{
- SECTION("table setting")
- {
- L.requireFailure("require 'lanes'.configure{[{}] = {}}");
- }
+ L.requireFailure("require 'lanes'.configure{[{}] = {}}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("boolean setting")
- {
- L.requireFailure("require 'lanes'.configure{[true] = 'gluh'}");
- }
+ SECTION("boolean setting")
+ {
+ L.requireFailure("require 'lanes'.configure{[true] = 'gluh'}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("function setting")
- {
- L.requireFailure("require 'lanes'.configure{[function() end] = 1}");
- }
+ SECTION("function setting")
+ {
+ L.requireFailure("require 'lanes'.configure{[function() end] = 1}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("number setting")
- {
- L.requireFailure("require 'lanes'.configure{[1] = function() end}");
- }
+ SECTION("number setting")
+ {
+ L.requireFailure("require 'lanes'.configure{[1] = function() end}");
+ }
- // -----------------------------------------------------------------------------------------
+ // ---------------------------------------------------------------------------------------------
- SECTION("unknown string setting")
- {
- L.requireFailure("require 'lanes'.configure{['gluh'] = false}");
- }
+ SECTION("unknown string setting")
+ {
+ L.requireFailure("require 'lanes'.configure{['gluh'] = false}");
}
}
diff --git a/unit_tests/linda_tests.cpp b/unit_tests/linda_tests.cpp
index f2934eb..9dbaa85 100644
--- a/unit_tests/linda_tests.cpp
+++ b/unit_tests/linda_tests.cpp
@@ -3,67 +3,132 @@
// #################################################################################################
-TEST_CASE("linda.single Keeper")
+TEST_CASE("linda.single_keeper.creation/no_argument")
{
LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
S.requireSuccess("lanes = require 'lanes'");
- SECTION("Linda creation")
- {
- // no parameters is ok
- S.requireSuccess("lanes.linda()");
- S.requireNotReturnedString("return tostring(lanes.linda())", R"===(Linda: )==="); // unspecified name should not result in
-
- // since we have only one keeper, only group 0 is authorized
- S.requireFailure("lanes.linda(-1)");
- S.requireSuccess("lanes.linda(0)");
- S.requireFailure("lanes.linda(1)");
-
- // any name is ok
- S.requireSuccess("lanes.linda('')"); // an empty name results in a string conversion of the form "Linda: " that we can't test (but it works)
- S.requireReturnedString("return tostring(lanes.linda('short name'))", R"===(Linda: short name)===");
- S.requireReturnedString("return tostring(lanes.linda('very very very very very very long name'))", R"===(Linda: very very very very very very long name)===");
- S.requireReturnedString("return tostring(lanes.linda('auto'))", R"===(Linda: [string "return tostring(lanes.linda('auto'))"]:1)===");
-
- if constexpr (LUA_VERSION_NUM == 504) {
- // a function is acceptable as a __close handler
- S.requireSuccess("local l = lanes.linda(function() end)");
- // a callable table too (a callable full userdata as well, but I have none here)
- S.requireSuccess("local l = lanes.linda(setmetatable({}, {__call = function() end}))");
- // if the function raises an error, we should get it
- S.requireFailure("local l = lanes.linda(function() error 'gluh' end)");
- } else {
- // no __close support before Lua 5.4
- S.requireFailure("lanes.linda(function() end)");
- S.requireFailure("lanes.linda(setmetatable({}, {__call = function() end}))");
- }
+ // no argument is ok
+ S.requireSuccess("lanes.linda()");
+ S.requireNotReturnedString("return tostring(lanes.linda())", R"===(Linda: )==="); // unspecified name should not result in
+}
- // mixing parameters in any order is ok: 2 out of 3
- S.requireSuccess("lanes.linda(0, 'name')");
- S.requireSuccess("lanes.linda('name', 0)");
- if constexpr (LUA_VERSION_NUM == 504) {
- S.requireSuccess("lanes.linda(0, function() end)");
- S.requireSuccess("lanes.linda(function() end, 0)");
- S.requireSuccess("lanes.linda('name', function() end)");
- S.requireSuccess("lanes.linda(function() end, 'name')");
- }
+// #################################################################################################
- // mixing parameters in any order is ok: 3 out of 3
- if constexpr (LUA_VERSION_NUM == 504) {
- S.requireSuccess("lanes.linda(0, 'name', function() end)");
- S.requireSuccess("lanes.linda(0, function() end, 'name')");
- S.requireSuccess("lanes.linda('name', 0, function() end)");
- S.requireSuccess("lanes.linda('name', function() end, 0)");
- S.requireSuccess("lanes.linda(function() end, 0, 'name')");
- S.requireSuccess("lanes.linda(function() end, 'name', 0)");
- }
+TEST_CASE("linda.single_keeper.creation/non_table_arguments")
+{
+ LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
+ S.requireSuccess("lanes = require 'lanes'");
+
+ // any argument that is not a table is not ok
+ S.requireFailure("lanes.linda(0)");
+ S.requireFailure("lanes.linda('bob')");
+ S.requireFailure("lanes.linda(false)");
+ S.requireFailure("lanes.linda(function() end)");
+ S.requireFailure("lanes.linda(lanes.cancel_error)");
+}
+
+// #################################################################################################
+
+TEST_CASE("linda.single_keeper.creation/close_handler")
+{
+ LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
+ S.requireSuccess("lanes = require 'lanes'");
+
+ if constexpr (LUA_VERSION_NUM == 504) {
+ // a function is acceptable as a __close handler
+ S.requireSuccess("local l = lanes.linda{close_handler = function() end}");
+ // a callable table too (a callable full userdata as well, but I have none here)
+ S.requireSuccess("local l = lanes.linda{close_handler = setmetatable({}, {__call = function() end})}");
+ } else {
+ // no __close support before Lua 5.4, field is ignored (therefore invalid values are accepted too!)
+ S.requireSuccess("lanes.linda{close_handler = 'a string'}");
+ S.requireSuccess("lanes.linda{close_handler = function() end}");
+ S.requireSuccess("lanes.linda{close_handler = setmetatable({}, {__call = function() end})}");
+ }
+}
+
+// #################################################################################################
+
+TEST_CASE("linda.single_keeper.creation/table_argument")
+{
+ LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
+ S.requireSuccess("lanes = require 'lanes'");
+
+ // one table is fine
+ S.requireSuccess("lanes.linda{}");
+ // anything beyond that is not
+ S.requireFailure("lanes.linda({},{})");
+ S.requireFailure("lanes.linda({},'bob')");
+ S.requireFailure("lanes.linda({},42)");
+}
+
+// #################################################################################################
+
+TEST_CASE("linda.single_keeper.creation/group")
+{
+ LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
+ S.requireSuccess("lanes = require 'lanes'");
+
+ // since we have only one keeper, only group 0 is authorized
+ S.requireFailure("lanes.linda{group = -1}");
+ S.requireSuccess("lanes.linda{group = 0}");
+ S.requireFailure("lanes.linda{group = 1}");
+}
+
+// #################################################################################################
+
+TEST_CASE("linda.single_keeper.creation/name")
+{
+ LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
+ S.requireSuccess("lanes = require 'lanes'");
+
+ // any name is ok
+ S.requireSuccess("lanes.linda{name = ''}"); // an empty name results in a string conversion of the form "Linda: " that we can't test (but it works)
+ S.requireReturnedString("return tostring(lanes.linda{name = 'short name'})", R"===(Linda: short name)===");
+ S.requireReturnedString("return tostring(lanes.linda{name = 'very very very very very very long name'})", R"===(Linda: very very very very very very long name)===");
+ S.requireReturnedString("return tostring(lanes.linda{name = 'auto'})", R"===(Linda: [string "return tostring(lanes.linda{name = 'auto'})"]:1)===");
+}
+
+// #################################################################################################
+
+TEST_CASE("linda.single_keeper.creation/wake_period")
+{
+ LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
+ S.requireSuccess("lanes = require 'lanes'");
- // unsupported parameters should fail
- S.requireFailure("lanes.linda(true)");
- S.requireFailure("lanes.linda(false)");
- // uncallable table or full userdata
- S.requireFailure("lanes.linda({})");
- S.requireFailure("lanes.linda(lanes.linda())");
+ // wake_period should be a number > 0
+ S.requireFailure("lanes.linda{wake_period = false}");
+ S.requireFailure("lanes.linda{wake_period = 'bob'}");
+ S.requireFailure("lanes.linda{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
+}
+
+// #################################################################################################
+
+TEST_CASE("linda.single_keeper.the_rest")
+{
+ LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } };
+ S.requireSuccess("lanes = require 'lanes'");
+
+ // ---------------------------------------------------------------------------------------------
+
+ SECTION("error in close handler is propagated")
+ {
+ // if the function raises an error, we should get it
+ S.requireFailure("local l = lanes.linda{close_handler = function() error 'gluh' end}");
}
// ---------------------------------------------------------------------------------------------
@@ -311,12 +376,12 @@ TEST_CASE("linda.multi Keeper")
S.requireSuccess("lanes = require 'lanes'.configure{nb_user_keepers = 3}");
- S.requireFailure("lanes.linda(-1)");
- S.requireSuccess("lanes.linda(0)");
- S.requireSuccess("lanes.linda(1)");
- S.requireSuccess("lanes.linda(2)");
- S.requireSuccess("lanes.linda(3)");
- S.requireFailure("lanes.linda(4)");
+ S.requireFailure("lanes.linda{group = -1}");
+ S.requireSuccess("lanes.linda{group = 0}");
+ S.requireSuccess("lanes.linda{group = 1}");
+ S.requireSuccess("lanes.linda{group = 2}");
+ S.requireSuccess("lanes.linda{group = 3}");
+ S.requireFailure("lanes.linda{group = 4}");
}
// #################################################################################################
diff --git a/unit_tests/scripts/lane/tasking_cancelling.lua b/unit_tests/scripts/lane/tasking_cancelling.lua
index ea4516e..873140e 100644
--- a/unit_tests/scripts/lane/tasking_cancelling.lua
+++ b/unit_tests/scripts/lane/tasking_cancelling.lua
@@ -16,7 +16,7 @@ local lanes_linda = assert(lanes.linda)
-- ##################################################################################################
-- cancellation of lanes waiting on a linda
-local limited = lanes_linda("limited")
+local limited = lanes_linda{name = "limited"}
assert.fails(function() limited:limit("key", -1) end)
assert.failsnot(function() limited:limit("key", 1) end)
-- [[################################################
diff --git a/unit_tests/scripts/lane/tasking_comms_criss_cross.lua b/unit_tests/scripts/lane/tasking_comms_criss_cross.lua
index 497e81d..610da8b 100644
--- a/unit_tests/scripts/lane/tasking_comms_criss_cross.lua
+++ b/unit_tests/scripts/lane/tasking_comms_criss_cross.lua
@@ -42,7 +42,7 @@ local tc = lanes_gen("io", { name = 'auto', gc_cb = gc_cb },
end
)
-local linda= lanes_linda("criss cross")
+local linda= lanes_linda{name = "criss cross"}
local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms
diff --git a/unit_tests/scripts/lane/tasking_communications.lua b/unit_tests/scripts/lane/tasking_communications.lua
index 631d105..01842b4 100644
--- a/unit_tests/scripts/lane/tasking_communications.lua
+++ b/unit_tests/scripts/lane/tasking_communications.lua
@@ -72,7 +72,7 @@ local chunk= function(linda)
WR("chunk ", "Lane ends!\n")
end
-local linda = lanes_linda("communications")
+local linda = lanes_linda{name = "communications"}
assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications")
--
-- ["->"] master -> slave
diff --git a/unit_tests/scripts/lane/tasking_send_receive_code.lua b/unit_tests/scripts/lane/tasking_send_receive_code.lua
index e329a88..cb3663f 100644
--- a/unit_tests/scripts/lane/tasking_send_receive_code.lua
+++ b/unit_tests/scripts/lane/tasking_send_receive_code.lua
@@ -65,7 +65,7 @@ local function chunk2(linda)
linda:send("up", function() return ":)" end, "ok2")
end
-local linda = lanes_linda("auto")
+local linda = lanes_linda{name = "auto"}
local t2= lanes_gen("debug,package,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch
linda:send("down", function(linda) linda:send("up", "ready!") end,
"ok")
diff --git a/unit_tests/scripts/linda/multiple_keepers.lua b/unit_tests/scripts/linda/multiple_keepers.lua
index 8733087..267d874 100644
--- a/unit_tests/scripts/linda/multiple_keepers.lua
+++ b/unit_tests/scripts/linda/multiple_keepers.lua
@@ -2,9 +2,9 @@
local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure{nb_user_keepers = 3, keepers_gc_threshold = 500}
local lanes = require_lanes_result_1
-local a = lanes.linda("A", 1)
-local b = lanes.linda("B", 2)
-local c = lanes.linda("C", 3)
+local a = lanes.linda{name = "A", group = 1}
+local b = lanes.linda{name = "B", group = 2}
+local c = lanes.linda{name = "C", group = 3}
-- store each linda in the other 2
do
diff --git a/unit_tests/scripts/linda/send_registered_userdata.lua b/unit_tests/scripts/linda/send_registered_userdata.lua
index 2c0195a..90c05c9 100644
--- a/unit_tests/scripts/linda/send_registered_userdata.lua
+++ b/unit_tests/scripts/linda/send_registered_userdata.lua
@@ -1,5 +1,5 @@
local lanes = require 'lanes'.configure{with_timers = false}
-local l = lanes.linda'gleh'
+local l = lanes.linda{name = 'gleh'}
l:set('yo', io.stdin)
local n, stdin_out = l:get('yo')
assert(n == 1 and stdin_out == io.stdin, tostring(stdin_out) .. " ~= " .. tostring(io.stdin))
--
cgit v1.2.3-55-g6feb
|