diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2025-04-18 10:26:19 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2025-04-18 10:26:19 +0200 |
commit | 022e40cc71beda874d0bad2cec227e472d5dd4af (patch) | |
tree | 7f0e03815217dd2e329a2ade1ed346247d5db884 | |
parent | 1bff784b474261212a996ac9fc59389d53a69590 (diff) | |
download | lanes-022e40cc71beda874d0bad2cec227e472d5dd4af.tar.gz lanes-022e40cc71beda874d0bad2cec227e472d5dd4af.tar.bz2 lanes-022e40cc71beda874d0bad2cec227e472d5dd4af.zip |
New feature: Linda periodical cancellation checks
* lanes.linda() api change: takes all settings in a single table argument
* new linda creation argument wake_period
* new lanes.configure setting linda_wake_period
* adjusted all unit tests (one TODO test fails on purpose)
Diffstat (limited to '')
-rw-r--r-- | docs/index.html | 46 | ||||
-rw-r--r-- | src/lanes.lua | 14 | ||||
-rw-r--r-- | src/linda.cpp | 152 | ||||
-rw-r--r-- | src/linda.hpp | 10 | ||||
-rw-r--r-- | src/lindafactory.cpp | 9 | ||||
-rw-r--r-- | src/universe.cpp | 8 | ||||
-rw-r--r-- | src/universe.hpp | 2 | ||||
-rw-r--r-- | tests/basic.lua | 8 | ||||
-rw-r--r-- | tests/cancel.lua | 2 | ||||
-rw-r--r-- | tests/deadlock.lua | 2 | ||||
-rw-r--r-- | tests/error.lua | 4 | ||||
-rw-r--r-- | tests/fifo.lua | 4 | ||||
-rw-r--r-- | tests/keeper.lua | 36 | ||||
-rw-r--r-- | tests/linda_perf.lua | 6 | ||||
-rw-r--r-- | tests/tobeclosed.lua | 12 | ||||
-rw-r--r-- | unit_tests/embedded_tests.cpp | 4 | ||||
-rw-r--r-- | unit_tests/init_and_shutdown.cpp | 533 | ||||
-rw-r--r-- | unit_tests/linda_tests.cpp | 185 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_cancelling.lua | 2 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_comms_criss_cross.lua | 2 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_communications.lua | 2 | ||||
-rw-r--r-- | unit_tests/scripts/lane/tasking_send_receive_code.lua | 2 | ||||
-rw-r--r-- | unit_tests/scripts/linda/multiple_keepers.lua | 6 | ||||
-rw-r--r-- | unit_tests/scripts/linda/send_registered_userdata.lua | 2 |
24 files changed, 627 insertions, 426 deletions
diff --git a/docs/index.html b/docs/index.html index 4dd5848..116fc0a 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -64,13 +64,13 @@ | |||
64 | <font size="-1"> | 64 | <font size="-1"> |
65 | <p> | 65 | <p> |
66 | <br /> | 66 | <br /> |
67 | <i>Copyright © 2007-24 Asko Kauppi, Benoit Germain. All rights reserved.</i> | 67 | <i>Copyright © 2007-25 Asko Kauppi, Benoit Germain. All rights reserved.</i> |
68 | <br /> | 68 | <br /> |
69 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, 5.3 and 5.4. | 69 | Lua Lanes is published under the same <a href="http://en.wikipedia.org/wiki/MIT_License">MIT license</a> as Lua 5.1, 5.2, 5.3 and 5.4. |
70 | </p> | 70 | </p> |
71 | 71 | ||
72 | <p> | 72 | <p> |
73 | This document was revised on 17-Mar-25, and applies to version <tt>4.0.0</tt>. | 73 | This document was revised on 18-Apr-25, and applies to version <tt>4.0.0</tt>. |
74 | </p> | 74 | </p> |
75 | </font> | 75 | </font> |
76 | </center> | 76 | </center> |
@@ -271,7 +271,7 @@ | |||
271 | </p> | 271 | </p> |
272 | 272 | ||
273 | <p> | 273 | <p> |
274 | <tt>lanes.configure</tt> accepts an optional options table as sole argument. | 274 | <tt>lanes.configure</tt> accepts an optional table as sole argument. |
275 | <table border="1" cellpadding="10" style="width:100%"> | 275 | <table border="1" cellpadding="10" style="width:100%"> |
276 | <tr> | 276 | <tr> |
277 | <th style="width:15%">name</th> | 277 | <th style="width:15%">name</th> |
@@ -337,6 +337,22 @@ | |||
337 | </tr> | 337 | </tr> |
338 | 338 | ||
339 | <tr valign=top> | 339 | <tr valign=top> |
340 | <td id="linda_wake_period"> | ||
341 | <code>.linda_wake_period</code> | ||
342 | </td> | ||
343 | <td> | ||
344 | number > 0 | ||
345 | </td> | ||
346 | <td> | ||
347 | Sets the default period in seconds a linda will wake by itself during blocked operations. Default is never.<br /> | ||
348 | When a Linda enters a blocking call (<tt>send()</tt>, <tt>receive()</tt>, <tt>receive_batched()</tt>, <tt>sleep()</tt>), it normally sleeps either until the operation completes | ||
349 | or the specified timeout expires. With this setting, the default behavior can be changed to wake periodically. This can help for example with timing issues where a lane is signalled | ||
350 | for cancellation, but a linda inside the lane was in the middle of processing an operation but did not actually start the wait. This can result in the signal to be ignored, thus | ||
351 | causing the Linda to wait out the full operation timeout before cancellation is processed. | ||
352 | </td> | ||
353 | </tr> | ||
354 | |||
355 | <tr valign=top> | ||
340 | <td id="nb_user_keepers"> | 356 | <td id="nb_user_keepers"> |
341 | <code>.nb_user_keepers</code> | 357 | <code>.nb_user_keepers</code> |
342 | </td> | 358 | </td> |
@@ -1222,7 +1238,7 @@ | |||
1222 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> | 1238 | <table border="1" bgcolor="#FFFFE0" cellpadding="10" style="width:50%"><tr><td><pre> |
1223 | local lanes = require "lanes" | 1239 | local lanes = require "lanes" |
1224 | 1240 | ||
1225 | local linda = lanes.linda("my linda") | 1241 | local linda = lanes.linda{name = "my linda"} |
1226 | 1242 | ||
1227 | local function loop(max) | 1243 | local function loop(max) |
1228 | for i = 1, max do | 1244 | for i = 1, max do |
@@ -1276,16 +1292,24 @@ | |||
1276 | </p> | 1292 | </p> |
1277 | 1293 | ||
1278 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1294 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1279 | h = lanes.linda([name],[group],[close_handler]) | 1295 | h = lanes.linda(table|nil) |
1280 | </pre></td></tr></table> | 1296 | </pre></td></tr></table> |
1281 | 1297 | ||
1282 | <p> | 1298 | <p> |
1283 | Arguments to <tt>lanes.linda()</tt> can be provided in any order, as long as there is a single string, a single number, and a single callable value (all are optional).<br /> | 1299 | Argument to <tt>lanes.linda()</tt> is either <tt>nil</tt> or a single table. The table may contain the following entries: |
1284 | Converting the linda to a string will yield the provided name prefixed by <tt>"Linda: "</tt>.<br /> | 1300 | <ul> |
1285 | If <tt>opt_name</tt> is omitted, it will evaluate to an hexadecimal number uniquely representing that linda when the linda is converted to a string. The value is the same as returned by <tt>linda:deep()</tt>.<br /> | 1301 | <li><tt>close_handler</tt>: a callable object (function or table/userdata with <tt>__call</tt> metamethod). If provided, and the linda is to-be-closed (Lua 5.4+), it will be called with all the provided arguments. For older Lua versions, its presence is ignored.</li> |
1286 | If <tt>opt_name</tt> is <tt>"auto"</tt>, Lanes will try to construct a name from the source location that called <tt>lanes.linda()</tt>. If that fails, the linda name will be <tt>"<unresolved>"</tt>.<br /> | 1302 | <li><tt>group</tt>: an integer between 0 and the number of Keeper states. Mandatory if Lanes is configured with more than one Keeper state. Group 0 is used by the internal timer linda.</li> |
1287 | If Lanes is configured with more than one Keeper state, <tt>group</tt> is mandatory.<br /> | 1303 | <li> |
1288 | If the linda is to-be-closed (Lua 5.4+), and a <tt>close_handler</tt> is provided, it will be called with all the provided arguments. For older Lua versions, its presence will cause an error. | 1304 | <tt>name</tt>: a string. Converting the linda to a string will yield the provided name prefixed by <tt>"Linda: "</tt>. |
1305 | If omitted or empty, it will evaluate to the string representation of a hexadecimal number uniquely representing that linda when the linda is converted to a string. The numeric value is the same as returned by <tt>linda:deep()</tt>.<br /> | ||
1306 | If <tt>"auto"</tt>, Lanes will try to construct a name from the source location that called <tt>lanes.linda()</tt>. If that fails, the linda name will be <tt>"<unresolved>"</tt>. | ||
1307 | </li> | ||
1308 | <li> | ||
1309 | <tt>wake_period</tt>: a number > 0 (unit: seconds). If provided, overrides <a href="#linda_wake_period"><tt>linda_wake_period</tt></a> provided to <a href="#initialization"><tt>lanes.configure()</tt></a>. | ||
1310 | </li> | ||
1311 | </ul> | ||
1312 | Unknown fields are silently ignored. | ||
1289 | </p> | 1313 | </p> |
1290 | 1314 | ||
1291 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1315 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
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 = | |||
96 | -- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation | 96 | -- it looks also like LuaJIT allocator may not appreciate direct use of its allocator for other purposes than the VM operation |
97 | internal_allocator = isLuaJIT and "libc" or "allocator", | 97 | internal_allocator = isLuaJIT and "libc" or "allocator", |
98 | keepers_gc_threshold = -1, | 98 | keepers_gc_threshold = -1, |
99 | linda_wake_period = 'never', | ||
99 | nb_user_keepers = 0, | 100 | nb_user_keepers = 0, |
100 | on_state_create = nil, | 101 | on_state_create = nil, |
101 | shutdown_timeout = 0.25, | 102 | shutdown_timeout = 0.25, |
@@ -141,6 +142,19 @@ local param_checkers = | |||
141 | end | 142 | end |
142 | return true | 143 | return true |
143 | end, | 144 | end, |
145 | linda_wake_period = function(val_) | ||
146 | -- linda_wake_period should be a number > 0, or the string 'never' | ||
147 | if val_ == 'never' then | ||
148 | return true | ||
149 | end | ||
150 | if type(val_) ~= "number" then | ||
151 | return nil, "not a number" | ||
152 | end | ||
153 | if val_ <= 0 then | ||
154 | return nil, "value out of range" | ||
155 | end | ||
156 | return true | ||
157 | end, | ||
144 | nb_user_keepers = function(val_) | 158 | nb_user_keepers = function(val_) |
145 | -- nb_user_keepers should be a number in [0,100] (so that nobody tries to run OOM by specifying a huge amount) | 159 | -- nb_user_keepers should be a number in [0,100] (so that nobody tries to run OOM by specifying a huge amount) |
146 | if type(val_) ~= "number" then | 160 | 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 { | |||
237 | _lane->waiting_on = &_linda->writeHappened; | 237 | _lane->waiting_on = &_linda->writeHappened; |
238 | _lane->status.store(Lane::Waiting, std::memory_order_release); | 238 | _lane->status.store(Lane::Waiting, std::memory_order_release); |
239 | } | 239 | } |
240 | |||
241 | // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests, | ||
242 | // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it | ||
243 | auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] { | ||
244 | auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
245 | if (wakePeriod.count() > 0.0f) { | ||
246 | _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod); | ||
247 | } | ||
248 | bool const _forceTryAgain{ _until_check_cancel < _until }; | ||
249 | return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until); | ||
250 | }); | ||
251 | |||
240 | // not enough data to read: wakeup when data was sent, or when timeout is reached | 252 | // not enough data to read: wakeup when data was sent, or when timeout is reached |
241 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; | 253 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; |
242 | std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until) }; | 254 | std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until_check_cancel) }; |
243 | _guard.release(); // we don't want to unlock the mutex on exit! | 255 | _guard.release(); // we don't want to unlock the mutex on exit! |
244 | _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups | 256 | _try_again = _forceTryAgain || (_status == std::cv_status::no_timeout); // detect spurious wakeups |
245 | if (_lane != nullptr) { | 257 | if (_lane != nullptr) { |
246 | _lane->waiting_on = nullptr; | 258 | _lane->waiting_on = nullptr; |
247 | _lane->status.store(_prev_status, std::memory_order_release); | 259 | _lane->status.store(_prev_status, std::memory_order_release); |
@@ -296,9 +308,10 @@ LUAG_FUNC(linda); | |||
296 | // ################################################################################################# | 308 | // ################################################################################################# |
297 | // ################################################################################################# | 309 | // ################################################################################################# |
298 | 310 | ||
299 | Linda::Linda(Universe* const U_, LindaGroup const group_, std::string_view const& name_) | 311 | Linda::Linda(Universe* const U_, std::string_view const& name_, lua_Duration const wake_period_, LindaGroup const group_) |
300 | : DeepPrelude{ LindaFactory::Instance } | 312 | : DeepPrelude{ LindaFactory::Instance } |
301 | , U{ U_ } | 313 | , U{ U_ } |
314 | , wakePeriod{ wake_period_ } | ||
302 | , keeperIndex{ group_ % U_->keepers.getNbKeepers() } | 315 | , keeperIndex{ group_ % U_->keepers.getNbKeepers() } |
303 | { | 316 | { |
304 | setName(name_); | 317 | setName(name_); |
@@ -330,9 +343,13 @@ Linda* Linda::CreateTimerLinda(lua_State* const L_) | |||
330 | STACK_CHECK_START_REL(L_, 0); // L_: | 343 | STACK_CHECK_START_REL(L_, 0); // L_: |
331 | // Initialize 'timerLinda'; a common Linda object shared by all states | 344 | // Initialize 'timerLinda'; a common Linda object shared by all states |
332 | lua_pushcfunction(L_, LG_linda); // L_: lanes.linda | 345 | lua_pushcfunction(L_, LG_linda); // L_: lanes.linda |
333 | luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda "lanes-timer" | 346 | lua_createtable(L_, 0, 3); // L_: lanes.linda {} |
334 | lua_pushinteger(L_, 0); // L_: lanes.linda "lanes-timer" 0 | 347 | luaG_pushstring(L_, "lanes-timer"); // L_: lanes.linda {} "lanes-timer" |
335 | lua_call(L_, 2, 1); // L_: linda | 348 | luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "name" }); // L_: lanes.linda { .name="lanes-timer" } |
349 | lua_pushinteger(L_, 0); // L_: lanes.linda { .name="lanes-timer" } 0 | ||
350 | luaG_setfield(L_, StackIndex{ -2 }, std::string_view{ "group" }); // L_: lanes.linda { .name="lanes-timer" .group = 0 } | ||
351 | // note that wake_period is not set (will default to the value in the universe) | ||
352 | lua_call(L_, 1, 1); // L_: linda | ||
336 | STACK_CHECK(L_, 1); | 353 | STACK_CHECK(L_, 1); |
337 | 354 | ||
338 | // Proxy userdata contents is only a 'DeepPrelude*' pointer | 355 | // Proxy userdata contents is only a 'DeepPrelude*' pointer |
@@ -941,11 +958,23 @@ LUAG_FUNC(linda_send) | |||
941 | _lane->waiting_on = &_linda->readHappened; | 958 | _lane->waiting_on = &_linda->readHappened; |
942 | _lane->status.store(Lane::Waiting, std::memory_order_release); | 959 | _lane->status.store(Lane::Waiting, std::memory_order_release); |
943 | } | 960 | } |
961 | |||
962 | // wait until the final target date by small increments, interrupting regularly so that we can check for cancel requests, | ||
963 | // in case some timing issue caused a cancel request to be issued, and the condvar signalled, before we actually wait for it | ||
964 | auto const [_forceTryAgain, _until_check_cancel] = std::invoke([_until, wakePeriod = _linda->getWakePeriod()] { | ||
965 | auto _until_check_cancel{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
966 | if (wakePeriod.count() > 0.0f) { | ||
967 | _until_check_cancel = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(wakePeriod); | ||
968 | } | ||
969 | bool const _forceTryAgain{ _until_check_cancel < _until }; | ||
970 | return std::make_tuple(_forceTryAgain, _forceTryAgain ? _until_check_cancel : _until); | ||
971 | }); | ||
972 | |||
944 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | 973 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached |
945 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; | 974 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; |
946 | std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until) }; | 975 | std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until_check_cancel) }; |
947 | _guard.release(); // we don't want to unlock the mutex on exit! | 976 | _guard.release(); // we don't want to unlock the mutex on exit! |
948 | _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups | 977 | _try_again = _forceTryAgain || (status == std::cv_status::no_timeout); // detect spurious wakeups |
949 | if (_lane != nullptr) { | 978 | if (_lane != nullptr) { |
950 | _lane->waiting_on = nullptr; | 979 | _lane->waiting_on = nullptr; |
951 | _lane->status.store(_prev_status, std::memory_order_release); | 980 | _lane->status.store(_prev_status, std::memory_order_release); |
@@ -1129,88 +1158,69 @@ namespace { | |||
1129 | // ################################################################################################# | 1158 | // ################################################################################################# |
1130 | 1159 | ||
1131 | /* | 1160 | /* |
1132 | * ud = lanes.linda( [name[,group[,close_handler]]]) | 1161 | * ud = lanes.linda{.name = <string>, .group = <number>, .close_handler = <callable>, .wake_period = <number>} |
1133 | * | 1162 | * |
1134 | * returns a linda object, or raises an error if creation failed | 1163 | * returns a linda object, or raises an error if creation failed |
1135 | */ | 1164 | */ |
1136 | LUAG_FUNC(linda) | 1165 | LUAG_FUNC(linda) |
1137 | { | 1166 | { |
1138 | static constexpr StackIndex kLastArg{ LUA_VERSION_NUM >= 504 ? 3 : 2 }; | 1167 | // unpack the received table on the stack, putting name wake_period group close_handler in that order |
1139 | StackIndex const _top{ lua_gettop(L_) }; | 1168 | StackIndex const _top{ lua_gettop(L_) }; |
1140 | luaL_argcheck(L_, _top <= kLastArg, _top, "too many arguments"); | 1169 | luaL_argcheck(L_, _top <= 1, _top, "too many arguments"); |
1141 | StackIndex _closeHandlerIdx{}; | 1170 | if (_top == 0) { |
1142 | StackIndex _nameIdx{}; | 1171 | lua_settop(L_, 3); // L_: nil nil nil |
1143 | StackIndex _groupIdx{}; | 1172 | } |
1144 | for (StackIndex const _i : std::ranges::iota_view{ StackIndex{ 1 }, StackIndex{ _top + 1 }}) { | 1173 | else if (!lua_istable(L_, kIdxTop)) { |
1145 | switch (luaG_type(L_, _i)) { | 1174 | luaL_argerror(L_, 1, "expecting a table"); |
1175 | } else { | ||
1176 | auto* const _U{ Universe::Get(L_) }; | ||
1177 | lua_getfield(L_, 1, "wake_period"); // L_: {} wake_period | ||
1178 | if (lua_isnil(L_, kIdxTop)) { | ||
1179 | lua_pop(L_, 1); | ||
1180 | lua_pushnumber(L_, _U->lindaWakePeriod.count()); | ||
1181 | } else { | ||
1182 | luaL_argcheck(L_, luaL_optnumber(L_, 2, 0) > 0, 1, "wake_period must be > 0"); | ||
1183 | } | ||
1184 | |||
1185 | lua_getfield(L_, 1, "group"); // L_: {} wake_period group | ||
1186 | int const _nbKeepers{ _U->keepers.getNbKeepers() }; | ||
1187 | if (lua_isnil(L_, kIdxTop)) { | ||
1188 | luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios"); | ||
1189 | } else { | ||
1190 | int const _group{ static_cast<int>(lua_tointeger(L_, kIdxTop)) }; | ||
1191 | luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, 1, "group out of range"); | ||
1192 | } | ||
1193 | |||
1146 | #if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4 | 1194 | #if LUA_VERSION_NUM >= 504 // to-be-closed support starts with Lua 5.4 |
1147 | case LuaType::FUNCTION: | 1195 | lua_getfield(L_, 1, "close_handler"); // L_: {} wake_period group close_handler |
1148 | luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler"); | 1196 | LuaType const _handlerType{ luaG_type(L_, kIdxTop) }; |
1149 | _closeHandlerIdx = _i; | 1197 | if (_handlerType == LuaType::NIL) { |
1150 | break; | 1198 | lua_pop(L_, 1); // L_: {} wake_period group |
1151 | 1199 | } else if (_handlerType == LuaType::USERDATA || _handlerType == LuaType::TABLE) { | |
1152 | case LuaType::USERDATA: | 1200 | luaL_argcheck(L_, luaL_getmetafield(L_, kIdxTop, "__call") != 0, 1, "__close handler is not callable"); |
1153 | case LuaType::TABLE: | ||
1154 | luaL_argcheck(L_, _closeHandlerIdx == 0, _i, "More than one __close handler"); | ||
1155 | luaL_argcheck(L_, luaL_getmetafield(L_, _i, "__call") != 0, _i, "__close handler is not callable"); | ||
1156 | lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it | 1201 | lua_pop(L_, 1); // luaL_getmetafield() pushed the field, we need to pop it |
1157 | _closeHandlerIdx = _i; | 1202 | } else { |
1158 | break; | 1203 | luaL_argcheck(L_, _handlerType == LuaType::FUNCTION, 1, "__close handler is not a function"); |
1204 | } | ||
1159 | #endif // LUA_VERSION_NUM >= 504 | 1205 | #endif // LUA_VERSION_NUM >= 504 |
1160 | 1206 | ||
1161 | case LuaType::STRING: | 1207 | auto const _nameType{ luaG_getfield(L_, StackIndex{ 1 }, "name") }; // L_: {} wake_period group [close_handler] name |
1162 | luaL_argcheck(L_, _nameIdx == 0, _i, "More than one name"); | 1208 | luaL_argcheck(L_, _nameType == LuaType::NIL || _nameType == LuaType::STRING, 1, "name is not a string"); |
1163 | _nameIdx = _i; | 1209 | lua_replace(L_, 1); // L_: name wake_period group [close_handler] |
1164 | break; | ||
1165 | |||
1166 | case LuaType::NUMBER: | ||
1167 | luaL_argcheck(L_, _groupIdx == 0, _i, "More than one group"); | ||
1168 | _groupIdx = _i; | ||
1169 | break; | ||
1170 | |||
1171 | default: | ||
1172 | luaL_argcheck(L_, false, _i, "Bad argument type (should be a string, a number, or a callable type)"); | ||
1173 | } | ||
1174 | } | ||
1175 | |||
1176 | int const _nbKeepers{ Universe::Get(L_)->keepers.getNbKeepers() }; | ||
1177 | if (!_groupIdx) { | ||
1178 | luaL_argcheck(L_, _nbKeepers < 2, 0, "Group is mandatory in multiple Keeper scenarios"); | ||
1179 | } else { | ||
1180 | int const _group{ static_cast<int>(lua_tointeger(L_, _groupIdx)) }; | ||
1181 | luaL_argcheck(L_, _group >= 0 && _group < _nbKeepers, _groupIdx, "Group out of range"); | ||
1182 | } | 1210 | } |
1183 | 1211 | ||
1184 | // done with argument checking, let's proceed | 1212 | // done with argument checking, let's proceed |
1185 | if constexpr (LUA_VERSION_NUM >= 504) { | 1213 | if (lua_gettop(L_) == 4) { |
1186 | // make sure we have kMaxArgs arguments on the stack for processing, with name, group, and handler, in that order | ||
1187 | lua_settop(L_, kLastArg); // L_: a b c | ||
1188 | // If either index is 0, lua_settop() adjusted the stack with a nil in slot kLastArg | ||
1189 | lua_pushvalue(L_, _closeHandlerIdx ? _closeHandlerIdx : kLastArg); // L_: a b c close_handler | ||
1190 | lua_pushvalue(L_, _groupIdx ? _groupIdx : kLastArg); // L_: a b c close_handler group | ||
1191 | lua_pushvalue(L_, _nameIdx ? _nameIdx : kLastArg); // L_: a b c close_handler group name | ||
1192 | lua_replace(L_, 1); // L_: name b c close_handler group | ||
1193 | lua_replace(L_, 2); // L_: name group c close_handler | ||
1194 | lua_replace(L_, 3); // L_: name group close_handler | ||
1195 | |||
1196 | // if we have a __close handler, we need a uservalue slot to store it | 1214 | // if we have a __close handler, we need a uservalue slot to store it |
1197 | UserValueCount const _nuv{ _closeHandlerIdx ? 1 : 0 }; | 1215 | LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 1 }); // L_: name wake_period group [close_handler] linda |
1198 | LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, _nuv); // L_: name group close_handler linda | 1216 | lua_replace(L_, 3); // L_: name wake_period linda close_handler |
1199 | if (_closeHandlerIdx != 0) { | 1217 | lua_setiuservalue(L_, StackIndex{ 3 }, UserValueIndex{ 1 }); // L_: name wake_period linda |
1200 | lua_replace(L_, 2); // L_: name linda close_handler | ||
1201 | lua_setiuservalue(L_, StackIndex{ 2 }, UserValueIndex{ 1 }); // L_: name linda | ||
1202 | } | ||
1203 | // depending on whether we have a handler or not, the stack is not in the same state at this point | 1218 | // depending on whether we have a handler or not, the stack is not in the same state at this point |
1204 | // just make sure we have our Linda at the top | 1219 | // just make sure we have our Linda at the top |
1205 | LUA_ASSERT(L_, ToLinda<true>(L_, kIdxTop)); | 1220 | LUA_ASSERT(L_, ToLinda<true>(L_, kIdxTop)); |
1206 | return 1; | 1221 | return 1; |
1207 | } else { // no to-be-closed support | 1222 | } else { // no to-be-closed support |
1208 | // ensure we have name, group in that order on the stack | 1223 | LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name wake_period group linda |
1209 | if (_nameIdx > _groupIdx) { | ||
1210 | lua_insert(L_, 1); // L_: name group | ||
1211 | } | ||
1212 | LindaFactory::Instance.pushDeepUserdata(DestState{ L_ }, UserValueCount{ 0 }); // L_: name group linda | ||
1213 | return 1; | 1224 | return 1; |
1214 | } | 1225 | } |
1215 | |||
1216 | } | 1226 | } |
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 | |||
42 | }; | 42 | }; |
43 | using enum Status; | 43 | using enum Status; |
44 | 44 | ||
45 | private: | 45 | public: |
46 | Universe* const U{ nullptr }; // the universe this linda belongs to | ||
46 | 47 | ||
48 | private: | ||
47 | static constexpr size_t kEmbeddedNameLength = 24; | 49 | static constexpr size_t kEmbeddedNameLength = 24; |
48 | using EmbeddedName = std::array<char, kEmbeddedNameLength>; | 50 | using EmbeddedName = std::array<char, kEmbeddedNameLength>; |
49 | // depending on the name length, it is either embedded inside the Linda, or allocated separately | 51 | // depending on the name length, it is either embedded inside the Linda, or allocated separately |
50 | std::variant<std::string_view, EmbeddedName> nameVariant{}; | 52 | std::variant<std::string_view, EmbeddedName> nameVariant{}; |
51 | // counts the keeper operations in progress | 53 | // counts the keeper operations in progress |
52 | std::atomic<int> keeperOperationCount{}; | 54 | std::atomic<int> keeperOperationCount{}; |
55 | lua_Duration wakePeriod{}; | ||
53 | 56 | ||
54 | public: | 57 | public: |
55 | std::condition_variable readHappened{}; | 58 | std::condition_variable readHappened{}; |
56 | std::condition_variable writeHappened{}; | 59 | std::condition_variable writeHappened{}; |
57 | Universe* const U{ nullptr }; // the universe this linda belongs to | ||
58 | KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda | 60 | KeeperIndex const keeperIndex{ -1 }; // the keeper associated to this linda |
59 | Status cancelStatus{ Status::Active }; | 61 | Status cancelStatus{ Status::Active }; |
60 | 62 | ||
@@ -68,7 +70,7 @@ class Linda final | |||
68 | static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internalAllocator.free(p_, sizeof(Linda)); } | 70 | static void operator delete(void* p_) { static_cast<Linda*>(p_)->U->internalAllocator.free(p_, sizeof(Linda)); } |
69 | 71 | ||
70 | ~Linda(); | 72 | ~Linda(); |
71 | Linda(Universe* U_, LindaGroup group_, std::string_view const& name_); | 73 | Linda(Universe* U_, std::string_view const& name_, lua_Duration wake_period_, LindaGroup group_); |
72 | Linda() = delete; | 74 | Linda() = delete; |
73 | // non-copyable, non-movable | 75 | // non-copyable, non-movable |
74 | Linda(Linda const&) = delete; | 76 | Linda(Linda const&) = delete; |
@@ -92,6 +94,8 @@ class Linda final | |||
92 | [[nodiscard]] | 94 | [[nodiscard]] |
93 | std::string_view getName() const; | 95 | std::string_view getName() const; |
94 | [[nodiscard]] | 96 | [[nodiscard]] |
97 | auto getWakePeriod() const { return wakePeriod; } | ||
98 | [[nodiscard]] | ||
95 | bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } | 99 | bool inKeeperOperation() const { return keeperOperationCount.load(std::memory_order_seq_cst) != 0; } |
96 | template <typename T = uintptr_t> | 100 | template <typename T = uintptr_t> |
97 | [[nodiscard]] | 101 | [[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 | |||
108 | 108 | ||
109 | DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const | 109 | DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const |
110 | { | 110 | { |
111 | // we always expect name and group at the bottom of the stack (either can be nil). any extra stuff we ignore and keep unmodified | 111 | STACK_CHECK_START_REL(L_, 0); |
112 | // 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 | ||
112 | std::string_view _linda_name{ luaG_tostring(L_, StackIndex{ 1 }) }; | 113 | std::string_view _linda_name{ luaG_tostring(L_, StackIndex{ 1 }) }; |
113 | LindaGroup _linda_group{ static_cast<int>(lua_tointeger(L_, 2)) }; | 114 | auto const _wake_period{ static_cast<lua_Duration>(lua_tonumber(L_, 2)) }; |
115 | LindaGroup const _linda_group{ static_cast<int>(lua_tointeger(L_, 3)) }; | ||
114 | 116 | ||
115 | // store in the linda the location of the script that created it | 117 | // store in the linda the location of the script that created it |
116 | if (_linda_name == "auto") { | 118 | if (_linda_name == "auto") { |
@@ -129,6 +131,7 @@ DeepPrelude* LindaFactory::newDeepObjectInternal(lua_State* const L_) const | |||
129 | // The deep data is allocated separately of Lua stack; we might no longer be around when last reference to it is being released. | 131 | // The deep data is allocated separately of Lua stack; we might no longer be around when last reference to it is being released. |
130 | // 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 | 132 | // 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 |
131 | Universe* const _U{ Universe::Get(L_) }; | 133 | Universe* const _U{ Universe::Get(L_) }; |
132 | Linda* const _linda{ new (_U) Linda{ _U, _linda_group, _linda_name } }; | 134 | Linda* const _linda{ new (_U) Linda{ _U, _linda_name, _wake_period, _linda_group } }; |
135 | STACK_CHECK(L_, 0); | ||
133 | return _linda; | 136 | return _linda; |
134 | } | 137 | } |
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_) | |||
153 | lua_setmetatable(L_, -2); // L_: settings universe | 153 | lua_setmetatable(L_, -2); // L_: settings universe |
154 | lua_pop(L_, 1); // L_: settings | 154 | lua_pop(L_, 1); // L_: settings |
155 | 155 | ||
156 | std::ignore = luaG_getfield(L_, kIdxSettings, "linda_wake_period"); // L_: settings linda_wake_period | ||
157 | if (luaG_type(L_, kIdxTop) == LuaType::NUMBER) { | ||
158 | _U->lindaWakePeriod = lua_Duration{ lua_tonumber(L_, kIdxTop) }; | ||
159 | } else { | ||
160 | LUA_ASSERT(L_, luaG_tostring(L_, kIdxTop) == "never"); | ||
161 | } | ||
162 | lua_pop(L_, 1); // L_: settings | ||
163 | |||
156 | std::ignore = luaG_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions | 164 | std::ignore = luaG_getfield(L_, kIdxSettings, "strip_functions"); // L_: settings strip_functions |
157 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; | 165 | _U->stripFunctions = lua_toboolean(L_, -1) ? true : false; |
158 | lua_pop(L_, 1); // L_: settings | 166 | 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 | |||
99 | 99 | ||
100 | Keepers keepers; | 100 | Keepers keepers; |
101 | 101 | ||
102 | lua_Duration lindaWakePeriod{}; | ||
103 | |||
102 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object | 104 | // Initialized by 'init_once_LOCKED()': the deep userdata Linda object |
103 | // used for timers (each lane will get a proxy to this) | 105 | // used for timers (each lane will get a proxy to this) |
104 | Linda* timerLinda{ nullptr }; | 106 | 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) | |||
163 | assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'") | 163 | assert(st == "cancelled", "st is '" .. st .. "' instead of 'cancelled'") |
164 | 164 | ||
165 | -- cancellation of lanes waiting on a linda | 165 | -- cancellation of lanes waiting on a linda |
166 | local limited = lanes_linda("limited") | 166 | local limited = lanes_linda{name = "limited"} |
167 | assert.fails(function() limited:limit("key", -1) end) | 167 | assert.fails(function() limited:limit("key", -1) end) |
168 | assert.failsnot(function() limited:limit("key", 1) end) | 168 | assert.failsnot(function() limited:limit("key", 1) end) |
169 | -- [[################################################ | 169 | -- [[################################################ |
@@ -255,7 +255,7 @@ local chunk= function(linda) | |||
255 | WR("chunk ", "Lane ends!\n") | 255 | WR("chunk ", "Lane ends!\n") |
256 | end | 256 | end |
257 | 257 | ||
258 | local linda = lanes_linda("communications") | 258 | local linda = lanes_linda{name = "communications"} |
259 | assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications") | 259 | assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications") |
260 | -- | 260 | -- |
261 | -- ["->"] master -> slave | 261 | -- ["->"] master -> slave |
@@ -410,7 +410,7 @@ local tc = lanes.gen("io", { name = 'auto', gc_cb = gc_cb }, | |||
410 | end | 410 | end |
411 | ) | 411 | ) |
412 | 412 | ||
413 | local linda= lanes_linda("criss cross") | 413 | local linda= lanes_linda{name = "criss cross"} |
414 | 414 | ||
415 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms | 415 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms |
416 | 416 | ||
@@ -461,7 +461,7 @@ local function chunk2(linda) | |||
461 | linda:send("up", function() return ":)" end, "ok2") | 461 | linda:send("up", function() return ":)" end, "ok2") |
462 | end | 462 | end |
463 | 463 | ||
464 | local linda = lanes_linda("auto") | 464 | local linda = lanes_linda{name = "auto"} |
465 | local t2 = lanes.gen("debug,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch | 465 | local t2 = lanes.gen("debug,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch |
466 | linda:send("down", function(linda) linda:send("up", "ready!") end, | 466 | linda:send("down", function(linda) linda:send("up", "ready!") end, |
467 | "ok") | 467 | "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(...) | |||
148 | local paramLessClosure = function() laneBody(unpack(params)) end | 148 | local paramLessClosure = function() laneBody(unpack(params)) end |
149 | local status, message = xpcall(paramLessClosure, errorHandler) | 149 | local status, message = xpcall(paramLessClosure, errorHandler) |
150 | if status == false then | 150 | if status == false then |
151 | print(" error handler rethrowing '" .. (ce == message and "cancel_error"or tostring(message)) .. "'") | 151 | print(" protectedBody rethrowing '" .. (ce == message and "cancel_error" or tostring(message)) .. "'") |
152 | -- if the error isn't rethrown, the lane's finalizer won't get it | 152 | -- if the error isn't rethrown, the lane's finalizer won't get it |
153 | error(message) | 153 | error(message) |
154 | end | 154 | 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" | |||
16 | local do_extra_stuff = true | 16 | local do_extra_stuff = true |
17 | 17 | ||
18 | if do_extra_stuff then | 18 | if do_extra_stuff then |
19 | local linda = lanes.linda "deadlock_linda" | 19 | local linda = lanes.linda{name = "deadlock_linda"} |
20 | -- just something to make send() succeed and receive() fail | 20 | -- just something to make send() succeed and receive() fail |
21 | local payload = { io.flush } | 21 | local payload = { io.flush } |
22 | 22 | ||
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 | |||
106 | 106 | ||
107 | local lane_error_as_string = "'lane error as string'" | 107 | local lane_error_as_string = "'lane error as string'" |
108 | local lane_error_as_table = setmetatable({"lane error as table"}, make_table_error_mt()) | 108 | local lane_error_as_table = setmetatable({"lane error as table"}, make_table_error_mt()) |
109 | local lane_error_as_linda = lanes.linda("'lane error'") | 109 | local lane_error_as_linda = lanes.linda{name = "'lane error'"} |
110 | 110 | ||
111 | local finalizer_error_as_string = "'finalizer error as string'" | 111 | local finalizer_error_as_string = "'finalizer error as string'" |
112 | local finalizer_error_as_table = setmetatable({"finalizer error as table"}, make_table_error_mt()) | 112 | local finalizer_error_as_table = setmetatable({"finalizer error as table"}, make_table_error_mt()) |
113 | local finalizer_error_as_linda = lanes.linda("'finalizer error'") | 113 | local finalizer_error_as_linda = lanes.linda{name = "'finalizer error'"} |
114 | 114 | ||
115 | local test_settings = {} | 115 | local test_settings = {} |
116 | local configure_tests = function() | 116 | 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 @@ | |||
6 | 6 | ||
7 | local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true} | 7 | local lanes = require "lanes".configure{shutdown_timeout=3,with_timers=true} |
8 | 8 | ||
9 | local atomic_linda = lanes.linda( "atom") | 9 | local atomic_linda = lanes.linda{name = "atom"} |
10 | local atomic_inc= lanes.genatomic( atomic_linda, "FIFO_n") | 10 | local atomic_inc= lanes.genatomic( atomic_linda, "FIFO_n") |
11 | 11 | ||
12 | local fifo_linda = lanes.linda( "fifo") | 12 | local fifo_linda = lanes.linda{name = "fifo"} |
13 | 13 | ||
14 | -- Lua 5.1 support | 14 | -- Lua 5.1 support |
15 | local table_unpack = table.unpack or unpack | 15 | 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 | |||
40 | end | 40 | end |
41 | 41 | ||
42 | -- should succeed | 42 | -- should succeed |
43 | assert.failsnot(function() createLinda("zero", 0) end) | 43 | assert.failsnot(function() createLinda{name = "zero", group = 0} end) |
44 | assert.failsnot(function() createLinda("one", 1) end) | 44 | assert.failsnot(function() createLinda{name = "one", group = 1} end) |
45 | assert.failsnot(function() createLinda("two", 2) end) | 45 | assert.failsnot(function() createLinda{name = "two", group = 2} end) |
46 | assert.failsnot(function() createLinda("three", 3) end) | 46 | assert.failsnot(function() createLinda{name = "three", group = 3} end) |
47 | -- should fail (and not create the lindas) | 47 | -- should fail (and not create the lindas) |
48 | assert.fails(function() createLinda("minus 1", -1) end) | 48 | assert.fails(function() createLinda{name = "minus 1", group = -1} end) |
49 | assert.fails(function() createLinda("none") end) | 49 | assert.fails(function() createLinda{name = "none"} end) |
50 | assert.fails(function() createLinda("four", 4) end) | 50 | assert.fails(function() createLinda{name = "four", group = 4} end) |
51 | 51 | ||
52 | end | 52 | end |
53 | -- should only collect the 4 successfully created lindas | 53 | -- should only collect the 4 successfully created lindas |
@@ -58,11 +58,11 @@ DONE() | |||
58 | if true then | 58 | if true then |
59 | PRINT "=========================================================================================" | 59 | PRINT "=========================================================================================" |
60 | PRINT "Linda names test:" | 60 | PRINT "Linda names test:" |
61 | local unnamedLinda1 = lanes.linda(1) | 61 | local unnamedLinda1 = lanes.linda{group = 1} |
62 | local unnamedLinda2 = lanes.linda("", 2) | 62 | local unnamedLinda2 = lanes.linda{name = "", group = 2} |
63 | local veeeerrrryyyylooongNamedLinda3 = lanes.linda( "veeeerrrryyyylooongNamedLinda", 3) | 63 | local veeeerrrryyyylooongNamedLinda3 = lanes.linda{ name = "veeeerrrryyyylooongNamedLinda", group = 3} |
64 | assert(tostring(veeeerrrryyyylooongNamedLinda3) == "Linda: veeeerrrryyyylooongNamedLinda") | 64 | assert(tostring(veeeerrrryyyylooongNamedLinda3) == "Linda: veeeerrrryyyylooongNamedLinda") |
65 | local shortNamedLinda0 = lanes.linda( "short", 0) | 65 | local shortNamedLinda0 = lanes.linda{name = "short", group = 0} |
66 | assert(tostring(shortNamedLinda0) == "Linda: short") | 66 | assert(tostring(shortNamedLinda0) == "Linda: short") |
67 | PRINT(shortNamedLinda0, unnamedLinda1, unnamedLinda2, veeeerrrryyyylooongNamedLinda3) | 67 | PRINT(shortNamedLinda0, unnamedLinda1, unnamedLinda2, veeeerrrryyyylooongNamedLinda3) |
68 | end | 68 | end |
@@ -74,12 +74,12 @@ DONE() | |||
74 | if true then | 74 | if true then |
75 | PRINT "=========================================================================================" | 75 | PRINT "=========================================================================================" |
76 | PRINT "Linda GC test:" | 76 | PRINT "Linda GC test:" |
77 | local a = lanes.linda("A", 1) | 77 | local a = lanes.linda{name = "A", group = 1} |
78 | local b = lanes.linda("B", 2) | 78 | local b = lanes.linda{name = "B", group = 2} |
79 | local c = lanes.linda("C", 3) | 79 | local c = lanes.linda{name = "C", group = 3} |
80 | 80 | ||
81 | -- store lindas in each other and in themselves | 81 | -- store lindas in each other and in themselves |
82 | a:set("here", lanes.linda("temporary linda", 0)) | 82 | a:set("here", lanes.linda{name = "temporary linda", group = 0}) |
83 | b:set("here", a, b, c) | 83 | b:set("here", a, b, c) |
84 | c:set("here", a, b, c) | 84 | c:set("here", a, b, c) |
85 | 85 | ||
@@ -120,13 +120,13 @@ if true then | |||
120 | end | 120 | end |
121 | 121 | ||
122 | -- | 122 | -- |
123 | local lindaA= lanes.linda( "A", 1) | 123 | local lindaA= lanes.linda{name = "A", group = 1} |
124 | local A= keeper( lindaA ) | 124 | local A= keeper( lindaA ) |
125 | 125 | ||
126 | local lindaB= lanes.linda( "B", 2) | 126 | local lindaB= lanes.linda{name = "B", group = 2} |
127 | local B= keeper( lindaB ) | 127 | local B= keeper( lindaB ) |
128 | 128 | ||
129 | local lindaC= lanes.linda( "C", 3) | 129 | local lindaC= lanes.linda{name = "C", group = 3} |
130 | local C= keeper( lindaC ) | 130 | local C= keeper( lindaC ) |
131 | PRINT("Created", lindaA, lindaB, lindaC) | 131 | PRINT("Created", lindaA, lindaB, lindaC) |
132 | 132 | ||
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 | |||
22 | do | 22 | do |
23 | print "############################################ tests get/set" | 23 | print "############################################ tests get/set" |
24 | -- linda:get throughput | 24 | -- linda:get throughput |
25 | local l = lanes.linda("get/set", 1) | 25 | local l = lanes.linda{name = "get/set", group = 1} |
26 | local batch = {} | 26 | local batch = {} |
27 | for i = 1,1000 do | 27 | for i = 1,1000 do |
28 | table.insert(batch, i) | 28 | table.insert(batch, i) |
@@ -90,7 +90,7 @@ local group_uid = 1 | |||
90 | local function ziva1( preloop, loop, batch) | 90 | local function ziva1( preloop, loop, batch) |
91 | -- prefill the linda a bit to increase fifo stress | 91 | -- prefill the linda a bit to increase fifo stress |
92 | local top = math.max( preloop, loop) | 92 | local top = math.max( preloop, loop) |
93 | local l = lanes.linda("ziva1("..preloop..":"..loop..":"..batch..")", group_uid) | 93 | local l = lanes.linda{name = "ziva1("..preloop..":"..loop..":"..batch..")", group = group_uid} |
94 | group_uid = (group_uid % config.nb_user_keepers) + 1 | 94 | group_uid = (group_uid % config.nb_user_keepers) + 1 |
95 | local t1 = lanes.now_secs() | 95 | local t1 = lanes.now_secs() |
96 | for i = 1, preloop do | 96 | for i = 1, preloop do |
@@ -165,7 +165,7 @@ end | |||
165 | 165 | ||
166 | -- sequential write/read (no parallelization involved) | 166 | -- sequential write/read (no parallelization involved) |
167 | local function ziva2( preloop, loop, batch) | 167 | local function ziva2( preloop, loop, batch) |
168 | local l = lanes.linda("ziva2("..preloop..":"..loop..":"..tostring(batch)..")", group_uid) | 168 | local l = lanes.linda{name = "ziva2("..preloop..":"..loop..":"..tostring(batch)..")", group = group_uid} |
169 | group_uid = (group_uid % config.nb_user_keepers) + 1 | 169 | group_uid = (group_uid % config.nb_user_keepers) + 1 |
170 | -- prefill the linda a bit to increase fifo stress | 170 | -- prefill the linda a bit to increase fifo stress |
171 | local top, step = math.max( preloop, loop), batch or 1 | 171 | 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 | |||
36 | WR("f closing ", linda_) | 36 | WR("f closing ", linda_) |
37 | closed_by_f = true | 37 | closed_by_f = true |
38 | end | 38 | end |
39 | local lf <close> = lanes.linda("closed by f", close_handler_f) | 39 | local lf <close> = lanes.linda{name = "closed by f", close_handler = close_handler_f} |
40 | 40 | ||
41 | local close_handler_t = setmetatable({}, | 41 | local close_handler_t = setmetatable({}, |
42 | { | 42 | { |
@@ -46,7 +46,7 @@ do | |||
46 | end | 46 | end |
47 | } | 47 | } |
48 | ) | 48 | ) |
49 | local lt <close> = lanes.linda("closed by t", close_handler_t) | 49 | local lt <close> = lanes.linda{name = "closed by t", close_handler = close_handler_t} |
50 | end | 50 | end |
51 | assert(closed_by_f == true) | 51 | assert(closed_by_f == true) |
52 | assert(closed_by_t == true) | 52 | assert(closed_by_t == true) |
@@ -58,13 +58,13 @@ end | |||
58 | WR "================================================================================================" | 58 | WR "================================================================================================" |
59 | WR "Through Linda" | 59 | WR "Through Linda" |
60 | do | 60 | do |
61 | local l = lanes.linda("channel") | 61 | local l = lanes.linda{name = "channel"} |
62 | 62 | ||
63 | local close_handler_f = function(linda_, err_) | 63 | local close_handler_f = function(linda_, err_) |
64 | WR("f closing ", linda_) | 64 | WR("f closing ", linda_) |
65 | linda_:set("closed", true) | 65 | linda_:set("closed", true) |
66 | end | 66 | end |
67 | local l_in = lanes.linda("voyager", close_handler_f) | 67 | local l_in = lanes.linda{name = "voyager", close_handler = close_handler_f} |
68 | l:set("trip", l_in) | 68 | l:set("trip", l_in) |
69 | 69 | ||
70 | do | 70 | do |
@@ -99,7 +99,7 @@ end | |||
99 | WR "================================================================================================" | 99 | WR "================================================================================================" |
100 | WR "Linda closing through Lane" | 100 | WR "Linda closing through Lane" |
101 | do | 101 | do |
102 | local l = lanes.linda("channel") | 102 | local l = lanes.linda{name = "channel"} |
103 | local lane_body = function(l_arg_) | 103 | local lane_body = function(l_arg_) |
104 | WR "In lane body" | 104 | WR "In lane body" |
105 | -- linda obtained through a linda | 105 | -- linda obtained through a linda |
@@ -114,7 +114,7 @@ do | |||
114 | local _count, _closed = linda_:get("closed") | 114 | local _count, _closed = linda_:get("closed") |
115 | linda_:set("closed", (_closed or 0) + 1) | 115 | linda_:set("closed", (_closed or 0) + 1) |
116 | end | 116 | end |
117 | local l_in = lanes.linda("voyager", close_handler_f) | 117 | local l_in = lanes.linda{name = "voyager", close_handler = close_handler_f} |
118 | l:set("trip", l_in) | 118 | l:set("trip", l_in) |
119 | 119 | ||
120 | do | 120 | 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") | |||
157 | // function with an upvalue | 157 | // function with an upvalue |
158 | std::string_view const _script{ | 158 | std::string_view const _script{ |
159 | " local lanes = require 'lanes'.configure{with_timers = false}" | 159 | " local lanes = require 'lanes'.configure{with_timers = false}" |
160 | " local l = lanes.linda'gleh'" | 160 | " local l = lanes.linda{name = 'gleh'}" |
161 | " local upvalue = 'oeauaoeuoeuaoeuaoeujaoefubycfjbycfybcfjybcfjybcfjbcf'" | 161 | " local upvalue = 'oeauaoeuoeuaoeuaoeujaoefubycfjbycfybcfjybcfjybcfjbcf'" |
162 | " local upvalued = function()" | 162 | " local upvalued = function()" |
163 | " return upvalue" | 163 | " return upvalue" |
@@ -183,7 +183,7 @@ TEST_CASE("lanes.embedding.with default allocator") | |||
183 | // try to send io.open into a linda, which fails if io base library is not loaded | 183 | // try to send io.open into a linda, which fails if io base library is not loaded |
184 | std::string_view const _script{ | 184 | std::string_view const _script{ |
185 | " local lanes = require 'lanes'" | 185 | " local lanes = require 'lanes'" |
186 | " local l = lanes.linda'gleh'" | 186 | " local l = lanes.linda{name = 'gleh'}" |
187 | " l:set('yo', io.open)" | 187 | " l:set('yo', io.open)" |
188 | " return 'SUCCESS'" | 188 | " return 'SUCCESS'" |
189 | }; | 189 | }; |
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") | |||
289 | 289 | ||
290 | // ################################################################################################# | 290 | // ################################################################################################# |
291 | 291 | ||
292 | TEST_CASE("lanes.configure.linda_wake_period") | ||
293 | { | ||
294 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
295 | |||
296 | // linda_wake_period should be a number > 0, or 'never' | ||
297 | |||
298 | SECTION("linda_wake_period = <table>") | ||
299 | { | ||
300 | L.requireFailure("require 'lanes'.configure{linda_wake_period = {}}"); | ||
301 | } | ||
302 | |||
303 | // --------------------------------------------------------------------------------------------- | ||
304 | |||
305 | SECTION("linda_wake_period = <string>") | ||
306 | { | ||
307 | L.requireFailure("require 'lanes'.configure{linda_wake_period = 'gluh'}"); | ||
308 | } | ||
309 | |||
310 | // --------------------------------------------------------------------------------------------- | ||
311 | |||
312 | SECTION("linda_wake_period = 'never'") | ||
313 | { | ||
314 | L.requireSuccess("require 'lanes'.configure{linda_wake_period = 'never'}"); | ||
315 | } | ||
316 | |||
317 | // --------------------------------------------------------------------------------------------- | ||
318 | |||
319 | SECTION("linda_wake_period = <negative number>") | ||
320 | { | ||
321 | L.requireFailure("require 'lanes'.configure{linda_wake_period = -0.001}"); | ||
322 | } | ||
323 | |||
324 | // --------------------------------------------------------------------------------------------- | ||
325 | |||
326 | SECTION("linda_wake_period = 0") | ||
327 | { | ||
328 | L.requireFailure("require 'lanes'.configure{linda_wake_period = 0}"); | ||
329 | } | ||
330 | |||
331 | // --------------------------------------------------------------------------------------------- | ||
332 | |||
333 | SECTION("linda_wake_period = 0.0001s") | ||
334 | { | ||
335 | L.requireSuccess("require 'lanes'.configure{linda_wake_period = 0.0001}"); | ||
336 | } | ||
337 | |||
338 | // --------------------------------------------------------------------------------------------- | ||
339 | |||
340 | SECTION("linda_wake_period = 1e30") | ||
341 | { | ||
342 | L.requireSuccess("require 'lanes'.configure{linda_wake_period = 1e30}"); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | // ################################################################################################# | ||
347 | |||
292 | TEST_CASE("lanes.configure.nb_user_keepers") | 348 | TEST_CASE("lanes.configure.nb_user_keepers") |
293 | { | 349 | { |
294 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | 350 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; |
@@ -300,35 +356,35 @@ TEST_CASE("lanes.configure.nb_user_keepers") | |||
300 | L.requireFailure("require 'lanes'.configure{nb_user_keepers = {}}"); | 356 | L.requireFailure("require 'lanes'.configure{nb_user_keepers = {}}"); |
301 | } | 357 | } |
302 | 358 | ||
303 | // ----------------------------------------------------------------------------------------- | 359 | // --------------------------------------------------------------------------------------------- |
304 | 360 | ||
305 | SECTION("nb_user_keepers = <string>") | 361 | SECTION("nb_user_keepers = <string>") |
306 | { | 362 | { |
307 | L.requireFailure("require 'lanes'.configure{nb_user_keepers = 'gluh'}"); | 363 | L.requireFailure("require 'lanes'.configure{nb_user_keepers = 'gluh'}"); |
308 | } | 364 | } |
309 | 365 | ||
310 | // ----------------------------------------------------------------------------------------- | 366 | // --------------------------------------------------------------------------------------------- |
311 | 367 | ||
312 | SECTION("nb_user_keepers = -1") | 368 | SECTION("nb_user_keepers = -1") |
313 | { | 369 | { |
314 | L.requireFailure("require 'lanes'.configure{nb_user_keepers = -1}"); | 370 | L.requireFailure("require 'lanes'.configure{nb_user_keepers = -1}"); |
315 | } | 371 | } |
316 | 372 | ||
317 | // ----------------------------------------------------------------------------------------- | 373 | // --------------------------------------------------------------------------------------------- |
318 | 374 | ||
319 | SECTION("nb_user_keepers = 0") | 375 | SECTION("nb_user_keepers = 0") |
320 | { | 376 | { |
321 | L.requireSuccess("require 'lanes'.configure{nb_user_keepers = 0}"); | 377 | L.requireSuccess("require 'lanes'.configure{nb_user_keepers = 0}"); |
322 | } | 378 | } |
323 | 379 | ||
324 | // ----------------------------------------------------------------------------------------- | 380 | // --------------------------------------------------------------------------------------------- |
325 | 381 | ||
326 | SECTION("nb_user_keepers = 1") | 382 | SECTION("nb_user_keepers = 1") |
327 | { | 383 | { |
328 | L.requireSuccess("require 'lanes'.configure{nb_user_keepers = 1}"); | 384 | L.requireSuccess("require 'lanes'.configure{nb_user_keepers = 1}"); |
329 | } | 385 | } |
330 | 386 | ||
331 | // ----------------------------------------------------------------------------------------- | 387 | // --------------------------------------------------------------------------------------------- |
332 | 388 | ||
333 | SECTION("nb_user_keepers = 100") | 389 | SECTION("nb_user_keepers = 100") |
334 | { | 390 | { |
@@ -345,340 +401,355 @@ TEST_CASE("lanes.configure.nb_user_keepers") | |||
345 | 401 | ||
346 | // ################################################################################################# | 402 | // ################################################################################################# |
347 | 403 | ||
348 | TEST_CASE("lanes.configure.the rest") | 404 | TEST_CASE("lanes.configure.on_state_create") |
349 | { | 405 | { |
350 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | 406 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; |
351 | 407 | ||
352 | // on_state_create should be a function, either C or Lua, without upvalues | 408 | // on_state_create should be a function, either C or Lua, without upvalues |
353 | 409 | ||
354 | SECTION("on_state_create") | 410 | SECTION("on_state_create = <table>") |
355 | { | 411 | { |
356 | SECTION("on_state_create = <table>") | 412 | L.requireFailure("require 'lanes'.configure{on_state_create = {}}"); |
357 | { | 413 | } |
358 | L.requireFailure("require 'lanes'.configure{on_state_create = {}}"); | ||
359 | } | ||
360 | 414 | ||
361 | // ----------------------------------------------------------------------------------------- | 415 | // --------------------------------------------------------------------------------------------- |
362 | 416 | ||
363 | SECTION("on_state_create = <string>") | 417 | SECTION("on_state_create = <string>") |
364 | { | 418 | { |
365 | L.requireFailure("require 'lanes'.configure{on_state_create = 'gluh'}"); | 419 | L.requireFailure("require 'lanes'.configure{on_state_create = 'gluh'}"); |
366 | } | 420 | } |
367 | 421 | ||
368 | // ----------------------------------------------------------------------------------------- | 422 | // --------------------------------------------------------------------------------------------- |
369 | 423 | ||
370 | SECTION("on_state_create = <number>") | 424 | SECTION("on_state_create = <number>") |
371 | { | 425 | { |
372 | L.requireFailure("require 'lanes'.configure{on_state_create = 1}"); | 426 | L.requireFailure("require 'lanes'.configure{on_state_create = 1}"); |
373 | } | 427 | } |
374 | 428 | ||
375 | // ----------------------------------------------------------------------------------------- | 429 | // --------------------------------------------------------------------------------------------- |
376 | 430 | ||
377 | SECTION("on_state_create = false") | 431 | SECTION("on_state_create = false") |
378 | { | 432 | { |
379 | L.requireFailure("require 'lanes'.configure{on_state_create = false}"); | 433 | L.requireFailure("require 'lanes'.configure{on_state_create = false}"); |
380 | } | 434 | } |
381 | 435 | ||
382 | // ----------------------------------------------------------------------------------------- | 436 | // --------------------------------------------------------------------------------------------- |
383 | 437 | ||
384 | SECTION("on_state_create = true") | 438 | SECTION("on_state_create = true") |
385 | { | 439 | { |
386 | L.requireFailure("require 'lanes'.configure{on_state_create = true}"); | 440 | L.requireFailure("require 'lanes'.configure{on_state_create = true}"); |
387 | } | 441 | } |
388 | 442 | ||
389 | // ----------------------------------------------------------------------------------------- | 443 | // --------------------------------------------------------------------------------------------- |
390 | 444 | ||
391 | SECTION("on_state_create = <Lua function>") | 445 | SECTION("on_state_create = <Lua function>") |
392 | { | 446 | { |
393 | // 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!) | 447 | // 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!) |
394 | L.requireSuccess("local print = print; require 'lanes'.configure{on_state_create = function() print 'hello' end}"); | 448 | L.requireSuccess("local print = print; require 'lanes'.configure{on_state_create = function() print 'hello' end}"); |
395 | } | 449 | } |
396 | 450 | ||
397 | // ----------------------------------------------------------------------------------------- | 451 | // --------------------------------------------------------------------------------------------- |
398 | 452 | ||
399 | SECTION("on_state_create = <C function>") | 453 | SECTION("on_state_create = <C function>") |
400 | { | 454 | { |
401 | // 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" | 455 | // 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" |
402 | // conclusion, don't use print() as a fake on_state_create() callback! | 456 | // conclusion, don't use print() as a fake on_state_create() callback! |
403 | // assert() should be fine since we pass a non-false argument to on_state_create | 457 | // assert() should be fine since we pass a non-false argument to on_state_create |
404 | L.requireSuccess("require 'lanes'.configure{on_state_create = assert}"); | 458 | L.requireSuccess("require 'lanes'.configure{on_state_create = assert}"); |
405 | } | ||
406 | } | 459 | } |
460 | } | ||
461 | |||
462 | // ################################################################################################# | ||
463 | |||
464 | TEST_CASE("lanes.configure.shutdown_timeout") | ||
465 | { | ||
466 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
407 | 467 | ||
408 | // --------------------------------------------------------------------------------------------- | ||
409 | // shutdown_timeout should be a number in [0,3600] | 468 | // shutdown_timeout should be a number in [0,3600] |
410 | 469 | ||
411 | SECTION("shutdown_timeout") | 470 | SECTION("shutdown_timeout = <table>") |
412 | { | 471 | { |
413 | SECTION("shutdown_timeout = <table>") | 472 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = {}}"); |
414 | { | 473 | } |
415 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = {}}"); | ||
416 | } | ||
417 | 474 | ||
418 | // ----------------------------------------------------------------------------------------- | 475 | // --------------------------------------------------------------------------------------------- |
419 | 476 | ||
420 | SECTION("shutdown_timeout = <string>") | 477 | SECTION("shutdown_timeout = <string>") |
421 | { | 478 | { |
422 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = 'gluh'}"); | 479 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = 'gluh'}"); |
423 | } | 480 | } |
424 | 481 | ||
425 | // ----------------------------------------------------------------------------------------- | 482 | // --------------------------------------------------------------------------------------------- |
426 | 483 | ||
427 | SECTION("shutdown_timeout = <negative number>") | 484 | SECTION("shutdown_timeout = <negative number>") |
428 | { | 485 | { |
429 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = -0.001}"); | 486 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = -0.001}"); |
430 | } | 487 | } |
431 | 488 | ||
432 | // ----------------------------------------------------------------------------------------- | 489 | // --------------------------------------------------------------------------------------------- |
433 | 490 | ||
434 | SECTION("shutdown_timeout = 0") | 491 | SECTION("shutdown_timeout = 0") |
435 | { | 492 | { |
436 | L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 0}"); | 493 | L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 0}"); |
437 | } | 494 | } |
438 | 495 | ||
439 | // ----------------------------------------------------------------------------------------- | 496 | // --------------------------------------------------------------------------------------------- |
440 | 497 | ||
441 | SECTION("shutdown_timeout = 1s") | 498 | SECTION("shutdown_timeout = 1s") |
442 | { | 499 | { |
443 | L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 1}"); | 500 | L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 1}"); |
444 | } | 501 | } |
445 | 502 | ||
446 | // ----------------------------------------------------------------------------------------- | 503 | // --------------------------------------------------------------------------------------------- |
447 | 504 | ||
448 | SECTION("shutdown_timeout = 3600s") | 505 | SECTION("shutdown_timeout = 3600s") |
449 | { | 506 | { |
450 | L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 3600}"); | 507 | L.requireSuccess("require 'lanes'.configure{shutdown_timeout = 3600}"); |
451 | } | 508 | } |
452 | 509 | ||
453 | // ----------------------------------------------------------------------------------------- | 510 | // --------------------------------------------------------------------------------------------- |
454 | 511 | ||
455 | SECTION("shutdown_timeout = <too long>") | 512 | SECTION("shutdown_timeout = <too long>") |
456 | { | 513 | { |
457 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = 3600.001}"); | 514 | L.requireFailure("require 'lanes'.configure{shutdown_timeout = 3600.001}"); |
458 | } | ||
459 | } | 515 | } |
516 | } | ||
517 | |||
518 | // ################################################################################################# | ||
519 | |||
520 | TEST_CASE("lanes.configure.strip_functions") | ||
521 | { | ||
522 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
460 | 523 | ||
461 | // --------------------------------------------------------------------------------------------- | ||
462 | // strip_functions should be a boolean | 524 | // strip_functions should be a boolean |
463 | 525 | ||
464 | SECTION("strip_functions") | 526 | SECTION("strip_functions = <table>") |
465 | { | 527 | { |
466 | SECTION("strip_functions = <table>") | 528 | L.requireFailure("require 'lanes'.configure{strip_functions = {}}"); |
467 | { | 529 | } |
468 | L.requireFailure("require 'lanes'.configure{strip_functions = {}}"); | ||
469 | } | ||
470 | 530 | ||
471 | // ----------------------------------------------------------------------------------------- | 531 | // --------------------------------------------------------------------------------------------- |
472 | 532 | ||
473 | SECTION("strip_functions = <string>") | 533 | SECTION("strip_functions = <string>") |
474 | { | 534 | { |
475 | L.requireFailure("require 'lanes'.configure{strip_functions = 'gluh'}"); | 535 | L.requireFailure("require 'lanes'.configure{strip_functions = 'gluh'}"); |
476 | } | 536 | } |
477 | 537 | ||
478 | // ----------------------------------------------------------------------------------------- | 538 | // --------------------------------------------------------------------------------------------- |
479 | 539 | ||
480 | SECTION("strip_functions = <number>") | 540 | SECTION("strip_functions = <number>") |
481 | { | 541 | { |
482 | L.requireFailure("require 'lanes'.configure{strip_functions = 1}"); | 542 | L.requireFailure("require 'lanes'.configure{strip_functions = 1}"); |
483 | } | 543 | } |
484 | 544 | ||
485 | // ----------------------------------------------------------------------------------------- | 545 | // --------------------------------------------------------------------------------------------- |
486 | 546 | ||
487 | SECTION("strip_functions = <C function>") | 547 | SECTION("strip_functions = <C function>") |
488 | { | 548 | { |
489 | L.requireFailure("require 'lanes'.configure{strip_functions = print}"); | 549 | L.requireFailure("require 'lanes'.configure{strip_functions = print}"); |
490 | } | 550 | } |
491 | 551 | ||
492 | // ----------------------------------------------------------------------------------------- | 552 | // --------------------------------------------------------------------------------------------- |
493 | 553 | ||
494 | SECTION("strip_functions = false") | 554 | SECTION("strip_functions = false") |
495 | { | 555 | { |
496 | L.requireSuccess("require 'lanes'.configure{strip_functions = false}"); | 556 | L.requireSuccess("require 'lanes'.configure{strip_functions = false}"); |
497 | } | 557 | } |
498 | 558 | ||
499 | // ----------------------------------------------------------------------------------------- | 559 | // --------------------------------------------------------------------------------------------- |
500 | 560 | ||
501 | SECTION("strip_functions = true") | 561 | SECTION("strip_functions = true") |
502 | { | 562 | { |
503 | L.requireSuccess("require 'lanes'.configure{strip_functions = true}"); | 563 | L.requireSuccess("require 'lanes'.configure{strip_functions = true}"); |
504 | } | ||
505 | } | 564 | } |
565 | } | ||
566 | |||
567 | // ################################################################################################# | ||
568 | |||
569 | TEST_CASE("lanes.configure.track_lanes") | ||
570 | { | ||
571 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
506 | 572 | ||
507 | // --------------------------------------------------------------------------------------------- | ||
508 | // track_lanes should be a boolean | 573 | // track_lanes should be a boolean |
509 | 574 | ||
510 | SECTION("track_lanes") | 575 | SECTION("track_lanes = <table>") |
511 | { | 576 | { |
512 | SECTION("track_lanes = <table>") | 577 | L.requireFailure("require 'lanes'.configure{track_lanes = {}}"); |
513 | { | 578 | } |
514 | L.requireFailure("require 'lanes'.configure{track_lanes = {}}"); | ||
515 | } | ||
516 | 579 | ||
517 | // ----------------------------------------------------------------------------------------- | 580 | // --------------------------------------------------------------------------------------------- |
518 | 581 | ||
519 | SECTION("track_lanes = <string>") | 582 | SECTION("track_lanes = <string>") |
520 | { | 583 | { |
521 | L.requireFailure("require 'lanes'.configure{track_lanes = 'gluh'}"); | 584 | L.requireFailure("require 'lanes'.configure{track_lanes = 'gluh'}"); |
522 | } | 585 | } |
523 | 586 | ||
524 | // ----------------------------------------------------------------------------------------- | 587 | // --------------------------------------------------------------------------------------------- |
525 | 588 | ||
526 | SECTION("track_lanes = <number>") | 589 | SECTION("track_lanes = <number>") |
527 | { | 590 | { |
528 | L.requireFailure("require 'lanes'.configure{track_lanes = 1}"); | 591 | L.requireFailure("require 'lanes'.configure{track_lanes = 1}"); |
529 | } | 592 | } |
530 | 593 | ||
531 | // ----------------------------------------------------------------------------------------- | 594 | // --------------------------------------------------------------------------------------------- |
532 | 595 | ||
533 | SECTION("track_lanes = <C function>") | 596 | SECTION("track_lanes = <C function>") |
534 | { | 597 | { |
535 | L.requireFailure("require 'lanes'.configure{track_lanes = print}"); | 598 | L.requireFailure("require 'lanes'.configure{track_lanes = print}"); |
536 | } | 599 | } |
537 | 600 | ||
538 | // ----------------------------------------------------------------------------------------- | 601 | // --------------------------------------------------------------------------------------------- |
539 | 602 | ||
540 | SECTION("track_lanes = false") | 603 | SECTION("track_lanes = false") |
541 | { | 604 | { |
542 | L.requireSuccess("require 'lanes'.configure{track_lanes = false}"); | 605 | L.requireSuccess("require 'lanes'.configure{track_lanes = false}"); |
543 | } | 606 | } |
544 | 607 | ||
545 | // ----------------------------------------------------------------------------------------- | 608 | // --------------------------------------------------------------------------------------------- |
546 | 609 | ||
547 | SECTION("track_lanes = true") | 610 | SECTION("track_lanes = true") |
548 | { | 611 | { |
549 | L.requireSuccess("require 'lanes'.configure{track_lanes = true}"); | 612 | L.requireSuccess("require 'lanes'.configure{track_lanes = true}"); |
550 | } | ||
551 | } | 613 | } |
614 | } | ||
615 | |||
616 | // ################################################################################################# | ||
617 | |||
618 | TEST_CASE("lanes.configure.verbose_errors") | ||
619 | { | ||
620 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
552 | 621 | ||
553 | // --------------------------------------------------------------------------------------------- | ||
554 | // verbose_errors should be a boolean | 622 | // verbose_errors should be a boolean |
555 | 623 | ||
556 | SECTION("verbose_errors") | 624 | SECTION("verbose_errors = <table>") |
557 | { | 625 | { |
558 | SECTION("verbose_errors = <table>") | 626 | L.requireFailure("require 'lanes'.configure{verbose_errors = {}}"); |
559 | { | 627 | } |
560 | L.requireFailure("require 'lanes'.configure{verbose_errors = {}}"); | ||
561 | } | ||
562 | 628 | ||
563 | // ----------------------------------------------------------------------------------------- | 629 | // --------------------------------------------------------------------------------------------- |
564 | 630 | ||
565 | SECTION("verbose_errors = <string>") | 631 | SECTION("verbose_errors = <string>") |
566 | { | 632 | { |
567 | L.requireFailure("require 'lanes'.configure{verbose_errors = 'gluh'}"); | 633 | L.requireFailure("require 'lanes'.configure{verbose_errors = 'gluh'}"); |
568 | } | 634 | } |
569 | 635 | ||
570 | // ----------------------------------------------------------------------------------------- | 636 | // --------------------------------------------------------------------------------------------- |
571 | 637 | ||
572 | SECTION("verbose_errors = <number>") | 638 | SECTION("verbose_errors = <number>") |
573 | { | 639 | { |
574 | L.requireFailure("require 'lanes'.configure{verbose_errors = 1}"); | 640 | L.requireFailure("require 'lanes'.configure{verbose_errors = 1}"); |
575 | } | 641 | } |
576 | 642 | ||
577 | // ----------------------------------------------------------------------------------------- | 643 | // --------------------------------------------------------------------------------------------- |
578 | 644 | ||
579 | SECTION("verbose_errors = <C function>") | 645 | SECTION("verbose_errors = <C function>") |
580 | { | 646 | { |
581 | L.requireFailure("require 'lanes'.configure{verbose_errors = print}"); | 647 | L.requireFailure("require 'lanes'.configure{verbose_errors = print}"); |
582 | } | 648 | } |
583 | 649 | ||
584 | // ----------------------------------------------------------------------------------------- | 650 | // --------------------------------------------------------------------------------------------- |
585 | 651 | ||
586 | SECTION("verbose_errors = false") | 652 | SECTION("verbose_errors = false") |
587 | { | 653 | { |
588 | L.requireSuccess("require 'lanes'.configure{verbose_errors = false}"); | 654 | L.requireSuccess("require 'lanes'.configure{verbose_errors = false}"); |
589 | } | 655 | } |
590 | 656 | ||
591 | // ----------------------------------------------------------------------------------------- | 657 | // --------------------------------------------------------------------------------------------- |
592 | 658 | ||
593 | SECTION("verbose_errors = true") | 659 | SECTION("verbose_errors = true") |
594 | { | 660 | { |
595 | L.requireSuccess("require 'lanes'.configure{verbose_errors = true}"); | 661 | L.requireSuccess("require 'lanes'.configure{verbose_errors = true}"); |
596 | } | ||
597 | } | 662 | } |
663 | } | ||
664 | |||
665 | // ################################################################################################# | ||
666 | |||
667 | TEST_CASE("lanes.configure.with_timers") | ||
668 | { | ||
669 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
598 | 670 | ||
599 | // --------------------------------------------------------------------------------------------- | ||
600 | // with_timers should be a boolean | 671 | // with_timers should be a boolean |
601 | 672 | ||
602 | SECTION("with_timers") | 673 | SECTION("with_timers = <table>") |
603 | { | 674 | { |
604 | SECTION("with_timers = <table>") | 675 | L.requireFailure("require 'lanes'.configure{with_timers = {}}"); |
605 | { | 676 | } |
606 | L.requireFailure("require 'lanes'.configure{with_timers = {}}"); | ||
607 | } | ||
608 | 677 | ||
609 | // ----------------------------------------------------------------------------------------- | 678 | // --------------------------------------------------------------------------------------------- |
610 | 679 | ||
611 | SECTION("with_timers = <string>") | 680 | SECTION("with_timers = <string>") |
612 | { | 681 | { |
613 | L.requireFailure("require 'lanes'.configure{with_timers = 'gluh'}"); | 682 | L.requireFailure("require 'lanes'.configure{with_timers = 'gluh'}"); |
614 | } | 683 | } |
615 | 684 | ||
616 | // ----------------------------------------------------------------------------------------- | 685 | // --------------------------------------------------------------------------------------------- |
617 | 686 | ||
618 | SECTION("with_timers = <number>") | 687 | SECTION("with_timers = <number>") |
619 | { | 688 | { |
620 | L.requireFailure("require 'lanes'.configure{with_timers = 1}"); | 689 | L.requireFailure("require 'lanes'.configure{with_timers = 1}"); |
621 | } | 690 | } |
622 | 691 | ||
623 | // ----------------------------------------------------------------------------------------- | 692 | // --------------------------------------------------------------------------------------------- |
624 | 693 | ||
625 | SECTION("with_timers = <C function>") | 694 | SECTION("with_timers = <C function>") |
626 | { | 695 | { |
627 | L.requireFailure("require 'lanes'.configure{with_timers = print}"); | 696 | L.requireFailure("require 'lanes'.configure{with_timers = print}"); |
628 | } | 697 | } |
629 | 698 | ||
630 | // ----------------------------------------------------------------------------------------- | 699 | // --------------------------------------------------------------------------------------------- |
631 | 700 | ||
632 | SECTION("with_timers = false") | 701 | SECTION("with_timers = false") |
633 | { | 702 | { |
634 | L.requireSuccess("require 'lanes'.configure{with_timers = false}"); | 703 | L.requireSuccess("require 'lanes'.configure{with_timers = false}"); |
635 | } | 704 | } |
636 | 705 | ||
637 | // ----------------------------------------------------------------------------------------- | 706 | // --------------------------------------------------------------------------------------------- |
638 | 707 | ||
639 | SECTION("with_timers = true") | 708 | SECTION("with_timers = true") |
640 | { | 709 | { |
641 | L.requireSuccess("require 'lanes'.configure{with_timers = true}"); | 710 | L.requireSuccess("require 'lanes'.configure{with_timers = true}"); |
642 | } | ||
643 | } | 711 | } |
712 | } | ||
713 | |||
714 | // ################################################################################################# | ||
715 | |||
716 | TEST_CASE("lanes.configure.unknown_setting") | ||
717 | { | ||
718 | LuaState L{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ false } }; | ||
644 | 719 | ||
645 | // --------------------------------------------------------------------------------------------- | ||
646 | // any unknown setting should be rejected | 720 | // any unknown setting should be rejected |
647 | 721 | ||
648 | SECTION("unknown_setting") | 722 | SECTION("table setting") |
649 | { | 723 | { |
650 | SECTION("table setting") | 724 | L.requireFailure("require 'lanes'.configure{[{}] = {}}"); |
651 | { | 725 | } |
652 | L.requireFailure("require 'lanes'.configure{[{}] = {}}"); | ||
653 | } | ||
654 | 726 | ||
655 | // ----------------------------------------------------------------------------------------- | 727 | // --------------------------------------------------------------------------------------------- |
656 | 728 | ||
657 | SECTION("boolean setting") | 729 | SECTION("boolean setting") |
658 | { | 730 | { |
659 | L.requireFailure("require 'lanes'.configure{[true] = 'gluh'}"); | 731 | L.requireFailure("require 'lanes'.configure{[true] = 'gluh'}"); |
660 | } | 732 | } |
661 | 733 | ||
662 | // ----------------------------------------------------------------------------------------- | 734 | // --------------------------------------------------------------------------------------------- |
663 | 735 | ||
664 | SECTION("function setting") | 736 | SECTION("function setting") |
665 | { | 737 | { |
666 | L.requireFailure("require 'lanes'.configure{[function() end] = 1}"); | 738 | L.requireFailure("require 'lanes'.configure{[function() end] = 1}"); |
667 | } | 739 | } |
668 | 740 | ||
669 | // ----------------------------------------------------------------------------------------- | 741 | // --------------------------------------------------------------------------------------------- |
670 | 742 | ||
671 | SECTION("number setting") | 743 | SECTION("number setting") |
672 | { | 744 | { |
673 | L.requireFailure("require 'lanes'.configure{[1] = function() end}"); | 745 | L.requireFailure("require 'lanes'.configure{[1] = function() end}"); |
674 | } | 746 | } |
675 | 747 | ||
676 | // ----------------------------------------------------------------------------------------- | 748 | // --------------------------------------------------------------------------------------------- |
677 | 749 | ||
678 | SECTION("unknown string setting") | 750 | SECTION("unknown string setting") |
679 | { | 751 | { |
680 | L.requireFailure("require 'lanes'.configure{['gluh'] = false}"); | 752 | L.requireFailure("require 'lanes'.configure{['gluh'] = false}"); |
681 | } | ||
682 | } | 753 | } |
683 | } | 754 | } |
684 | 755 | ||
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 @@ | |||
3 | 3 | ||
4 | // ################################################################################################# | 4 | // ################################################################################################# |
5 | 5 | ||
6 | TEST_CASE("linda.single Keeper") | 6 | TEST_CASE("linda.single_keeper.creation/no_argument") |
7 | { | 7 | { |
8 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | 8 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; |
9 | S.requireSuccess("lanes = require 'lanes'"); | 9 | S.requireSuccess("lanes = require 'lanes'"); |
10 | 10 | ||
11 | SECTION("Linda creation") | 11 | // no argument is ok |
12 | { | 12 | S.requireSuccess("lanes.linda()"); |
13 | // no parameters is ok | 13 | S.requireNotReturnedString("return tostring(lanes.linda())", R"===(Linda: <not a string>)==="); // unspecified name should not result in <not a string> |
14 | S.requireSuccess("lanes.linda()"); | 14 | } |
15 | S.requireNotReturnedString("return tostring(lanes.linda())", R"===(Linda: <not a string>)==="); // unspecified name should not result in <not a string> | ||
16 | |||
17 | // since we have only one keeper, only group 0 is authorized | ||
18 | S.requireFailure("lanes.linda(-1)"); | ||
19 | S.requireSuccess("lanes.linda(0)"); | ||
20 | S.requireFailure("lanes.linda(1)"); | ||
21 | |||
22 | // any name is ok | ||
23 | S.requireSuccess("lanes.linda('')"); // an empty name results in a string conversion of the form "Linda: <some hex value>" that we can't test (but it works) | ||
24 | S.requireReturnedString("return tostring(lanes.linda('short name'))", R"===(Linda: short name)==="); | ||
25 | S.requireReturnedString("return tostring(lanes.linda('very very very very very very long name'))", R"===(Linda: very very very very very very long name)==="); | ||
26 | S.requireReturnedString("return tostring(lanes.linda('auto'))", R"===(Linda: [string "return tostring(lanes.linda('auto'))"]:1)==="); | ||
27 | |||
28 | if constexpr (LUA_VERSION_NUM == 504) { | ||
29 | // a function is acceptable as a __close handler | ||
30 | S.requireSuccess("local l <close> = lanes.linda(function() end)"); | ||
31 | // a callable table too (a callable full userdata as well, but I have none here) | ||
32 | S.requireSuccess("local l <close> = lanes.linda(setmetatable({}, {__call = function() end}))"); | ||
33 | // if the function raises an error, we should get it | ||
34 | S.requireFailure("local l <close> = lanes.linda(function() error 'gluh' end)"); | ||
35 | } else { | ||
36 | // no __close support before Lua 5.4 | ||
37 | S.requireFailure("lanes.linda(function() end)"); | ||
38 | S.requireFailure("lanes.linda(setmetatable({}, {__call = function() end}))"); | ||
39 | } | ||
40 | 15 | ||
41 | // mixing parameters in any order is ok: 2 out of 3 | 16 | // ################################################################################################# |
42 | S.requireSuccess("lanes.linda(0, 'name')"); | ||
43 | S.requireSuccess("lanes.linda('name', 0)"); | ||
44 | if constexpr (LUA_VERSION_NUM == 504) { | ||
45 | S.requireSuccess("lanes.linda(0, function() end)"); | ||
46 | S.requireSuccess("lanes.linda(function() end, 0)"); | ||
47 | S.requireSuccess("lanes.linda('name', function() end)"); | ||
48 | S.requireSuccess("lanes.linda(function() end, 'name')"); | ||
49 | } | ||
50 | 17 | ||
51 | // mixing parameters in any order is ok: 3 out of 3 | 18 | TEST_CASE("linda.single_keeper.creation/non_table_arguments") |
52 | if constexpr (LUA_VERSION_NUM == 504) { | 19 | { |
53 | S.requireSuccess("lanes.linda(0, 'name', function() end)"); | 20 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; |
54 | S.requireSuccess("lanes.linda(0, function() end, 'name')"); | 21 | S.requireSuccess("lanes = require 'lanes'"); |
55 | S.requireSuccess("lanes.linda('name', 0, function() end)"); | 22 | |
56 | S.requireSuccess("lanes.linda('name', function() end, 0)"); | 23 | // any argument that is not a table is not ok |
57 | S.requireSuccess("lanes.linda(function() end, 0, 'name')"); | 24 | S.requireFailure("lanes.linda(0)"); |
58 | S.requireSuccess("lanes.linda(function() end, 'name', 0)"); | 25 | S.requireFailure("lanes.linda('bob')"); |
59 | } | 26 | S.requireFailure("lanes.linda(false)"); |
27 | S.requireFailure("lanes.linda(function() end)"); | ||
28 | S.requireFailure("lanes.linda(lanes.cancel_error)"); | ||
29 | } | ||
30 | |||
31 | // ################################################################################################# | ||
32 | |||
33 | TEST_CASE("linda.single_keeper.creation/close_handler") | ||
34 | { | ||
35 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | ||
36 | S.requireSuccess("lanes = require 'lanes'"); | ||
37 | |||
38 | if constexpr (LUA_VERSION_NUM == 504) { | ||
39 | // a function is acceptable as a __close handler | ||
40 | S.requireSuccess("local l <close> = lanes.linda{close_handler = function() end}"); | ||
41 | // a callable table too (a callable full userdata as well, but I have none here) | ||
42 | S.requireSuccess("local l <close> = lanes.linda{close_handler = setmetatable({}, {__call = function() end})}"); | ||
43 | } else { | ||
44 | // no __close support before Lua 5.4, field is ignored (therefore invalid values are accepted too!) | ||
45 | S.requireSuccess("lanes.linda{close_handler = 'a string'}"); | ||
46 | S.requireSuccess("lanes.linda{close_handler = function() end}"); | ||
47 | S.requireSuccess("lanes.linda{close_handler = setmetatable({}, {__call = function() end})}"); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | // ################################################################################################# | ||
52 | |||
53 | TEST_CASE("linda.single_keeper.creation/table_argument") | ||
54 | { | ||
55 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | ||
56 | S.requireSuccess("lanes = require 'lanes'"); | ||
57 | |||
58 | // one table is fine | ||
59 | S.requireSuccess("lanes.linda{}"); | ||
60 | // anything beyond that is not | ||
61 | S.requireFailure("lanes.linda({},{})"); | ||
62 | S.requireFailure("lanes.linda({},'bob')"); | ||
63 | S.requireFailure("lanes.linda({},42)"); | ||
64 | } | ||
65 | |||
66 | // ################################################################################################# | ||
67 | |||
68 | TEST_CASE("linda.single_keeper.creation/group") | ||
69 | { | ||
70 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | ||
71 | S.requireSuccess("lanes = require 'lanes'"); | ||
72 | |||
73 | // since we have only one keeper, only group 0 is authorized | ||
74 | S.requireFailure("lanes.linda{group = -1}"); | ||
75 | S.requireSuccess("lanes.linda{group = 0}"); | ||
76 | S.requireFailure("lanes.linda{group = 1}"); | ||
77 | } | ||
78 | |||
79 | // ################################################################################################# | ||
80 | |||
81 | TEST_CASE("linda.single_keeper.creation/name") | ||
82 | { | ||
83 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | ||
84 | S.requireSuccess("lanes = require 'lanes'"); | ||
85 | |||
86 | // any name is ok | ||
87 | S.requireSuccess("lanes.linda{name = ''}"); // an empty name results in a string conversion of the form "Linda: <some hex value>" that we can't test (but it works) | ||
88 | S.requireReturnedString("return tostring(lanes.linda{name = 'short name'})", R"===(Linda: short name)==="); | ||
89 | 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)==="); | ||
90 | S.requireReturnedString("return tostring(lanes.linda{name = 'auto'})", R"===(Linda: [string "return tostring(lanes.linda{name = 'auto'})"]:1)==="); | ||
91 | } | ||
92 | |||
93 | // ################################################################################################# | ||
94 | |||
95 | TEST_CASE("linda.single_keeper.creation/wake_period") | ||
96 | { | ||
97 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | ||
98 | S.requireSuccess("lanes = require 'lanes'"); | ||
60 | 99 | ||
61 | // unsupported parameters should fail | 100 | // wake_period should be a number > 0 |
62 | S.requireFailure("lanes.linda(true)"); | 101 | S.requireFailure("lanes.linda{wake_period = false}"); |
63 | S.requireFailure("lanes.linda(false)"); | 102 | S.requireFailure("lanes.linda{wake_period = 'bob'}"); |
64 | // uncallable table or full userdata | 103 | S.requireFailure("lanes.linda{wake_period = {}}"); |
65 | S.requireFailure("lanes.linda({})"); | 104 | S.requireFailure("lanes.linda{wake_period = -1}"); |
66 | S.requireFailure("lanes.linda(lanes.linda())"); | 105 | S.requireFailure("lanes.linda{wake_period = 0}"); |
106 | S.requireSuccess("lanes.linda{wake_period = 0.0001}"); | ||
107 | } | ||
108 | |||
109 | // ################################################################################################# | ||
110 | |||
111 | TEST_CASE("linda.single_keeper.wake_period") | ||
112 | { | ||
113 | FAIL("TODO: check that wake_period works as expected"); | ||
114 | // - use configure default if not provided | ||
115 | // - overrides default when provided | ||
116 | // - blocking operation wakes at the specified period | ||
117 | } | ||
118 | |||
119 | // ################################################################################################# | ||
120 | |||
121 | TEST_CASE("linda.single_keeper.the_rest") | ||
122 | { | ||
123 | LuaState S{ LuaState::WithBaseLibs{ true }, LuaState::WithFixture{ true } }; | ||
124 | S.requireSuccess("lanes = require 'lanes'"); | ||
125 | |||
126 | // --------------------------------------------------------------------------------------------- | ||
127 | |||
128 | SECTION("error in close handler is propagated") | ||
129 | { | ||
130 | // if the function raises an error, we should get it | ||
131 | S.requireFailure("local l <close> = lanes.linda{close_handler = function() error 'gluh' end}"); | ||
67 | } | 132 | } |
68 | 133 | ||
69 | // --------------------------------------------------------------------------------------------- | 134 | // --------------------------------------------------------------------------------------------- |
@@ -311,12 +376,12 @@ TEST_CASE("linda.multi Keeper") | |||
311 | 376 | ||
312 | S.requireSuccess("lanes = require 'lanes'.configure{nb_user_keepers = 3}"); | 377 | S.requireSuccess("lanes = require 'lanes'.configure{nb_user_keepers = 3}"); |
313 | 378 | ||
314 | S.requireFailure("lanes.linda(-1)"); | 379 | S.requireFailure("lanes.linda{group = -1}"); |
315 | S.requireSuccess("lanes.linda(0)"); | 380 | S.requireSuccess("lanes.linda{group = 0}"); |
316 | S.requireSuccess("lanes.linda(1)"); | 381 | S.requireSuccess("lanes.linda{group = 1}"); |
317 | S.requireSuccess("lanes.linda(2)"); | 382 | S.requireSuccess("lanes.linda{group = 2}"); |
318 | S.requireSuccess("lanes.linda(3)"); | 383 | S.requireSuccess("lanes.linda{group = 3}"); |
319 | S.requireFailure("lanes.linda(4)"); | 384 | S.requireFailure("lanes.linda{group = 4}"); |
320 | } | 385 | } |
321 | 386 | ||
322 | // ################################################################################################# | 387 | // ################################################################################################# |
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) | |||
16 | -- ################################################################################################## | 16 | -- ################################################################################################## |
17 | 17 | ||
18 | -- cancellation of lanes waiting on a linda | 18 | -- cancellation of lanes waiting on a linda |
19 | local limited = lanes_linda("limited") | 19 | local limited = lanes_linda{name = "limited"} |
20 | assert.fails(function() limited:limit("key", -1) end) | 20 | assert.fails(function() limited:limit("key", -1) end) |
21 | assert.failsnot(function() limited:limit("key", 1) end) | 21 | assert.failsnot(function() limited:limit("key", 1) end) |
22 | -- [[################################################ | 22 | -- [[################################################ |
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 }, | |||
42 | end | 42 | end |
43 | ) | 43 | ) |
44 | 44 | ||
45 | local linda= lanes_linda("criss cross") | 45 | local linda= lanes_linda{name = "criss cross"} |
46 | 46 | ||
47 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms | 47 | local a,b= tc(linda, "A","B"), tc(linda, "B","A") -- launching two lanes, twisted comms |
48 | 48 | ||
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) | |||
72 | WR("chunk ", "Lane ends!\n") | 72 | WR("chunk ", "Lane ends!\n") |
73 | end | 73 | end |
74 | 74 | ||
75 | local linda = lanes_linda("communications") | 75 | local linda = lanes_linda{name = "communications"} |
76 | assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications") | 76 | assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications") |
77 | -- | 77 | -- |
78 | -- ["->"] master -> slave | 78 | -- ["->"] 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) | |||
65 | linda:send("up", function() return ":)" end, "ok2") | 65 | linda:send("up", function() return ":)" end, "ok2") |
66 | end | 66 | end |
67 | 67 | ||
68 | local linda = lanes_linda("auto") | 68 | local linda = lanes_linda{name = "auto"} |
69 | local t2= lanes_gen("debug,package,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch | 69 | local t2= lanes_gen("debug,package,string,io", { name = 'auto', gc_cb = gc_cb }, chunk2)(linda) -- prepare & launch |
70 | linda:send("down", function(linda) linda:send("up", "ready!") end, | 70 | linda:send("down", function(linda) linda:send("up", "ready!") end, |
71 | "ok") | 71 | "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 @@ | |||
2 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure{nb_user_keepers = 3, keepers_gc_threshold = 500} | 2 | local require_lanes_result_1, require_lanes_result_2 = require "lanes".configure{nb_user_keepers = 3, keepers_gc_threshold = 500} |
3 | local lanes = require_lanes_result_1 | 3 | local lanes = require_lanes_result_1 |
4 | 4 | ||
5 | local a = lanes.linda("A", 1) | 5 | local a = lanes.linda{name = "A", group = 1} |
6 | local b = lanes.linda("B", 2) | 6 | local b = lanes.linda{name = "B", group = 2} |
7 | local c = lanes.linda("C", 3) | 7 | local c = lanes.linda{name = "C", group = 3} |
8 | 8 | ||
9 | -- store each linda in the other 2 | 9 | -- store each linda in the other 2 |
10 | do | 10 | 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 @@ | |||
1 | local lanes = require 'lanes'.configure{with_timers = false} | 1 | local lanes = require 'lanes'.configure{with_timers = false} |
2 | local l = lanes.linda'gleh' | 2 | local l = lanes.linda{name = 'gleh'} |
3 | l:set('yo', io.stdin) | 3 | l:set('yo', io.stdin) |
4 | local n, stdin_out = l:get('yo') | 4 | local n, stdin_out = l:get('yo') |
5 | assert(n == 1 and stdin_out == io.stdin, tostring(stdin_out) .. " ~= " .. tostring(io.stdin)) | 5 | assert(n == 1 and stdin_out == io.stdin, tostring(stdin_out) .. " ~= " .. tostring(io.stdin)) |