diff options
| -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)) |
