diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-14 17:41:05 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-14 17:41:05 +0200 |
| commit | 780f069d10b120968ae5867cc7e5a0da1ed05054 (patch) | |
| tree | 99dabf0e3759637fbe0134e59a5ec98d8c2ffae9 /src | |
| parent | 50f4c40bdb5667aa7053434dede2dd98f6f9e243 (diff) | |
| download | lanes-780f069d10b120968ae5867cc7e5a0da1ed05054.tar.gz lanes-780f069d10b120968ae5867cc7e5a0da1ed05054.tar.bz2 lanes-780f069d10b120968ae5867cc7e5a0da1ed05054.zip | |
Boyscouting
Diffstat (limited to 'src')
| -rw-r--r-- | src/linda.cpp | 741 | ||||
| -rw-r--r-- | src/lindafactory.cpp | 3 | ||||
| -rw-r--r-- | src/nameof.cpp | 1 | ||||
| -rw-r--r-- | src/state.cpp | 14 | ||||
| -rw-r--r-- | src/tools.cpp | 12 | ||||
| -rw-r--r-- | src/tools.h | 2 |
6 files changed, 398 insertions, 375 deletions
diff --git a/src/linda.cpp b/src/linda.cpp index 1933b06..4edc029 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
| @@ -38,75 +38,86 @@ THE SOFTWARE. | |||
| 38 | #include "tools.h" | 38 | #include "tools.h" |
| 39 | 39 | ||
| 40 | // ################################################################################################# | 40 | // ################################################################################################# |
| 41 | // ################################################################################################# | ||
| 42 | namespace { | ||
| 43 | // ############################################################################################# | ||
| 44 | // ############################################################################################# | ||
| 41 | 45 | ||
| 42 | static void check_key_types(lua_State* const L_, int const start_, int const end_) | ||
| 43 | { | ||
| 44 | for (int const _i : std::ranges::iota_view{ start_, end_ + 1 }) { | ||
| 45 | switch (LuaType const _t{ luaG_type(L_, _i) }) { | ||
| 46 | case LuaType::BOOLEAN: | ||
| 47 | case LuaType::NUMBER: | ||
| 48 | case LuaType::STRING: | ||
| 49 | break; | ||
| 50 | 46 | ||
| 51 | case LuaType::LIGHTUSERDATA: | 47 | static void CheckKeyTypes(lua_State* const L_, int const start_, int const end_) |
| 52 | { | 48 | { |
| 53 | static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; | 49 | for (int const _i : std::ranges::iota_view{ start_, end_ + 1 }) { |
| 54 | for (UniqueKey const& _key : kKeysToCheck) { | 50 | switch (LuaType const _t{ luaG_type(L_, _i) }) { |
| 55 | if (_key.equals(L_, _i)) { | 51 | case LuaType::BOOLEAN: |
| 56 | raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, _key.debugName.data()); | 52 | case LuaType::NUMBER: |
| 57 | break; | 53 | case LuaType::STRING: |
| 54 | break; | ||
| 55 | |||
| 56 | case LuaType::LIGHTUSERDATA: | ||
| 57 | { | ||
| 58 | static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; | ||
| 59 | for (UniqueKey const& _key : kKeysToCheck) { | ||
| 60 | if (_key.equals(L_, _i)) { | ||
| 61 | raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, _key.debugName.data()); | ||
| 62 | break; | ||
| 63 | } | ||
| 58 | } | 64 | } |
| 59 | } | 65 | } |
| 66 | break; | ||
| 67 | |||
| 68 | default: | ||
| 69 | raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i); | ||
| 60 | } | 70 | } |
| 61 | break; | 71 | } |
| 72 | } | ||
| 62 | 73 | ||
| 63 | default: | 74 | // ############################################################################################# |
| 64 | raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i); | 75 | |
| 76 | /* | ||
| 77 | * string = linda:__tostring( linda_ud) | ||
| 78 | * | ||
| 79 | * Return the stringification of a linda | ||
| 80 | * | ||
| 81 | * Useful for concatenation or debugging purposes | ||
| 82 | */ | ||
| 83 | |||
| 84 | template <bool OPT> | ||
| 85 | [[nodiscard]] static int LindaToString(lua_State* const L_, int const idx_) | ||
| 86 | { | ||
| 87 | Linda* const _linda{ ToLinda<OPT>(L_, idx_) }; | ||
| 88 | if (_linda != nullptr) { | ||
| 89 | luaG_pushstring(L_, "Linda: "); | ||
| 90 | std::string_view const _lindaName{ _linda->getName() }; | ||
| 91 | if (!_lindaName.empty()) { | ||
| 92 | luaG_pushstring(L_, _lindaName); | ||
| 93 | } else { | ||
| 94 | // obfuscate the pointer so that we can't read the value with our eyes out of a script | ||
| 95 | luaG_pushstring(L_, "%p", _linda->obfuscated()); | ||
| 96 | } | ||
| 97 | lua_concat(L_, 2); | ||
| 98 | return 1; | ||
| 65 | } | 99 | } |
| 100 | return 0; | ||
| 66 | } | 101 | } |
| 67 | } | ||
| 68 | 102 | ||
| 69 | // ################################################################################################# | 103 | // ############################################################################################# |
| 70 | 104 | ||
| 71 | /* | 105 | template <bool OPT> |
| 72 | * string = linda:__tostring( linda_ud) | 106 | [[nodiscard]] static inline Linda* ToLinda(lua_State* const L_, int const idx_) |
| 73 | * | 107 | { |
| 74 | * Return the stringification of a linda | 108 | Linda* const _linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) }; |
| 75 | * | 109 | if constexpr (!OPT) { |
| 76 | * Useful for concatenation or debugging purposes | 110 | luaL_argcheck(L_, _linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr |
| 77 | */ | 111 | LUA_ASSERT(L_, _linda->U == Universe::Get(L_)); |
| 78 | |||
| 79 | template <bool OPT> | ||
| 80 | [[nodiscard]] static int LindaToString(lua_State* L_, int idx_) | ||
| 81 | { | ||
| 82 | Linda* const _linda{ ToLinda<OPT>(L_, idx_) }; | ||
| 83 | if (_linda != nullptr) { | ||
| 84 | luaG_pushstring(L_, "Linda: "); | ||
| 85 | std::string_view const _lindaName{ _linda->getName() }; | ||
| 86 | if (!_lindaName.empty()) { | ||
| 87 | luaG_pushstring(L_, _lindaName); | ||
| 88 | } else { | ||
| 89 | // obfuscate the pointer so that we can't read the value with our eyes out of a script | ||
| 90 | luaG_pushstring(L_, "%p", _linda->obfuscated()); | ||
| 91 | } | 112 | } |
| 92 | lua_concat(L_, 2); | 113 | return _linda; |
| 93 | return 1; | ||
| 94 | } | 114 | } |
| 95 | return 0; | ||
| 96 | } | ||
| 97 | 115 | ||
| 116 | // ############################################################################################# | ||
| 117 | // ############################################################################################# | ||
| 118 | } // namespace | ||
| 119 | // ################################################################################################# | ||
| 98 | // ################################################################################################# | 120 | // ################################################################################################# |
| 99 | |||
| 100 | template <bool OPT> | ||
| 101 | [[nodiscard]] static inline Linda* ToLinda(lua_State* L_, int idx_) | ||
| 102 | { | ||
| 103 | Linda* const _linda{ static_cast<Linda*>(LindaFactory::Instance.toDeep(L_, idx_)) }; | ||
| 104 | if constexpr (!OPT) { | ||
| 105 | luaL_argcheck(L_, _linda != nullptr, idx_, "expecting a linda object"); // doesn't return if linda is nullptr | ||
| 106 | LUA_ASSERT(L_, _linda->U == Universe::Get(L_)); | ||
| 107 | } | ||
| 108 | return _linda; | ||
| 109 | } | ||
| 110 | 121 | ||
| 111 | // ################################################################################################# | 122 | // ################################################################################################# |
| 112 | // ################################################################################################# | 123 | // ################################################################################################# |
| @@ -114,7 +125,7 @@ template <bool OPT> | |||
| 114 | // ################################################################################################# | 125 | // ################################################################################################# |
| 115 | // ################################################################################################# | 126 | // ################################################################################################# |
| 116 | 127 | ||
| 117 | Linda::Linda(Universe* U_, LindaGroup group_, std::string_view const& name_) | 128 | Linda::Linda(Universe* const U_, LindaGroup const group_, std::string_view const& name_) |
| 118 | : DeepPrelude{ LindaFactory::Instance } | 129 | : DeepPrelude{ LindaFactory::Instance } |
| 119 | , U{ U_ } | 130 | , U{ U_ } |
| 120 | , keeperIndex{ group_ % U_->keepers.getNbKeepers() } | 131 | , keeperIndex{ group_ % U_->keepers.getNbKeepers() } |
| @@ -170,7 +181,7 @@ std::string_view Linda::getName() const | |||
| 170 | // ################################################################################################# | 181 | // ################################################################################################# |
| 171 | 182 | ||
| 172 | // used to perform all linda operations that access keepers | 183 | // used to perform all linda operations that access keepers |
| 173 | int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_) | 184 | int Linda::ProtectedCall(lua_State* const L_, lua_CFunction const f_) |
| 174 | { | 185 | { |
| 175 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 186 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
| 176 | 187 | ||
| @@ -344,14 +355,16 @@ LUAG_FUNC(linda_concat) | |||
| 344 | */ | 355 | */ |
| 345 | LUAG_FUNC(linda_count) | 356 | LUAG_FUNC(linda_count) |
| 346 | { | 357 | { |
| 347 | auto _count = [](lua_State* L_) { | 358 | static constexpr lua_CFunction _count{ |
| 348 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 359 | +[](lua_State* const L_) { |
| 349 | // make sure the keys are of a valid type | 360 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
| 350 | check_key_types(L_, 2, lua_gettop(L_)); | 361 | // make sure the keys are of a valid type |
| 351 | 362 | CheckKeyTypes(L_, 2, lua_gettop(L_)); | |
| 352 | Keeper* const _keeper{ _linda->whichKeeper() }; | 363 | |
| 353 | KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(count), L_, _linda, 2) }; | 364 | Keeper* const _keeper{ _linda->whichKeeper() }; |
| 354 | return OptionalValue(_pushed, L_, "tried to count an invalid key"); | 365 | KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(count), L_, _linda, 2) }; |
| 366 | return OptionalValue(_pushed, L_, "tried to count an invalid key"); | ||
| 367 | } | ||
| 355 | }; | 368 | }; |
| 356 | return Linda::ProtectedCall(L_, _count); | 369 | return Linda::ProtectedCall(L_, _count); |
| 357 | } | 370 | } |
| @@ -383,9 +396,11 @@ LUAG_FUNC(linda_deep) | |||
| 383 | */ | 396 | */ |
| 384 | LUAG_FUNC(linda_dump) | 397 | LUAG_FUNC(linda_dump) |
| 385 | { | 398 | { |
| 386 | auto _dump = [](lua_State* L_) { | 399 | static constexpr lua_CFunction _dump{ |
| 387 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 400 | +[](lua_State* const L_) { |
| 388 | return Keeper::PushLindaStorage(*_linda, DestState{ L_ }); | 401 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
| 402 | return Keeper::PushLindaStorage(*_linda, DestState{ L_ }); | ||
| 403 | } | ||
| 389 | }; | 404 | }; |
| 390 | return Linda::ProtectedCall(L_, _dump); | 405 | return Linda::ProtectedCall(L_, _dump); |
| 391 | } | 406 | } |
| @@ -399,28 +414,30 @@ LUAG_FUNC(linda_dump) | |||
| 399 | */ | 414 | */ |
| 400 | LUAG_FUNC(linda_get) | 415 | LUAG_FUNC(linda_get) |
| 401 | { | 416 | { |
| 402 | auto get = [](lua_State* L_) { | 417 | static constexpr lua_CFunction _get{ |
| 403 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 418 | +[](lua_State* const L_) { |
| 404 | lua_Integer const _count{ luaL_optinteger(L_, 3, 1) }; | 419 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
| 405 | luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1"); | 420 | lua_Integer const _count{ luaL_optinteger(L_, 3, 1) }; |
| 406 | luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments"); | 421 | luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1"); |
| 407 | // make sure the key is of a valid type (throws an error if not the case) | 422 | luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments"); |
| 408 | check_key_types(L_, 2, 2); | 423 | // make sure the key is of a valid type (throws an error if not the case) |
| 409 | 424 | CheckKeyTypes(L_, 2, 2); | |
| 410 | KeeperCallResult _pushed; | 425 | |
| 411 | if (_linda->cancelRequest == CancelRequest::None) { | 426 | KeeperCallResult _pushed; |
| 412 | Keeper* const _keeper{ _linda->whichKeeper() }; | 427 | if (_linda->cancelRequest == CancelRequest::None) { |
| 413 | _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, 2); | 428 | Keeper* const _keeper{ _linda->whichKeeper() }; |
| 414 | } else { // linda is cancelled | 429 | _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, 2); |
| 415 | // do nothing and return nil,lanes.cancel_error | 430 | } else { // linda is cancelled |
| 416 | lua_pushnil(L_); | 431 | // do nothing and return nil,lanes.cancel_error |
| 417 | kCancelError.pushKey(L_); | 432 | lua_pushnil(L_); |
| 418 | _pushed.emplace(2); | 433 | kCancelError.pushKey(L_); |
| 434 | _pushed.emplace(2); | ||
| 435 | } | ||
| 436 | // an error can be raised if we attempt to read an unregistered function | ||
| 437 | return OptionalValue(_pushed, L_, "tried to copy unsupported types"); | ||
| 419 | } | 438 | } |
| 420 | // an error can be raised if we attempt to read an unregistered function | ||
| 421 | return OptionalValue(_pushed, L_, "tried to copy unsupported types"); | ||
| 422 | }; | 439 | }; |
| 423 | return Linda::ProtectedCall(L_, get); | 440 | return Linda::ProtectedCall(L_, _get); |
| 424 | } | 441 | } |
| 425 | 442 | ||
| 426 | // ################################################################################################# | 443 | // ################################################################################################# |
| @@ -434,35 +451,37 @@ LUAG_FUNC(linda_get) | |||
| 434 | */ | 451 | */ |
| 435 | LUAG_FUNC(linda_limit) | 452 | LUAG_FUNC(linda_limit) |
| 436 | { | 453 | { |
| 437 | auto _limit = [](lua_State* L_) { | 454 | static constexpr lua_CFunction _limit{ |
| 438 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 455 | +[](lua_State* const L_) { |
| 439 | // make sure we got 3 arguments: the linda, a key and a limit | 456 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
| 440 | int const _nargs{ lua_gettop(L_) }; | 457 | // make sure we got 3 arguments: the linda, a key and a limit |
| 441 | luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); | 458 | int const _nargs{ lua_gettop(L_) }; |
| 442 | // make sure we got a numeric limit | 459 | luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); |
| 443 | lua_Integer const _limit{ luaL_optinteger(L_, 3, 0) }; | 460 | // make sure we got a numeric limit |
| 444 | if (_limit < 0) { | 461 | lua_Integer const _val{ luaL_optinteger(L_, 3, 0) }; |
| 445 | raise_luaL_argerror(L_, 3, "limit must be >= 0"); | 462 | if (_val < 0) { |
| 446 | } | 463 | raise_luaL_argerror(L_, 3, "limit must be >= 0"); |
| 447 | // make sure the key is of a valid type | ||
| 448 | check_key_types(L_, 2, 2); | ||
| 449 | |||
| 450 | KeeperCallResult _pushed; | ||
| 451 | if (_linda->cancelRequest == CancelRequest::None) { | ||
| 452 | Keeper* const _keeper{ _linda->whichKeeper() }; | ||
| 453 | _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2); | ||
| 454 | LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, -1) == LuaType::BOOLEAN); // no error, boolean value saying if we should wake blocked writer threads | ||
| 455 | if (lua_toboolean(L_, -1)) { | ||
| 456 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | ||
| 457 | } | 464 | } |
| 458 | } else { // linda is cancelled | 465 | // make sure the key is of a valid type |
| 459 | // do nothing and return nil,lanes.cancel_error | 466 | CheckKeyTypes(L_, 2, 2); |
| 460 | lua_pushnil(L_); | 467 | |
| 461 | kCancelError.pushKey(L_); | 468 | KeeperCallResult _pushed; |
| 462 | _pushed.emplace(2); | 469 | if (_linda->cancelRequest == CancelRequest::None) { |
| 470 | Keeper* const _keeper{ _linda->whichKeeper() }; | ||
| 471 | _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2); | ||
| 472 | LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, -1) == LuaType::BOOLEAN); // no error, boolean value saying if we should wake blocked writer threads | ||
| 473 | if (lua_toboolean(L_, -1)) { | ||
| 474 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | ||
| 475 | } | ||
| 476 | } else { // linda is cancelled | ||
| 477 | // do nothing and return nil,lanes.cancel_error | ||
| 478 | lua_pushnil(L_); | ||
| 479 | kCancelError.pushKey(L_); | ||
| 480 | _pushed.emplace(2); | ||
| 481 | } | ||
| 482 | // propagate pushed boolean if any | ||
| 483 | return _pushed.value(); | ||
| 463 | } | 484 | } |
| 464 | // propagate pushed boolean if any | ||
| 465 | return _pushed.value(); | ||
| 466 | }; | 485 | }; |
| 467 | return Linda::ProtectedCall(L_, _limit); | 486 | return Linda::ProtectedCall(L_, _limit); |
| 468 | } | 487 | } |
| @@ -481,197 +500,63 @@ LUAG_FUNC(linda_limit) | |||
| 481 | */ | 500 | */ |
| 482 | LUAG_FUNC(linda_receive) | 501 | LUAG_FUNC(linda_receive) |
| 483 | { | 502 | { |
| 484 | auto _receive = [](lua_State* L_) { | 503 | static constexpr lua_CFunction _receive{ |
| 485 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 504 | +[](lua_State* const L_) { |
| 486 | int _key_i{ 2 }; // index of first key, if timeout not there | 505 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
| 487 | 506 | int _key_i{ 2 }; // index of first key, if timeout not there | |
| 488 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | 507 | |
| 489 | if (luaG_type(L_, 2) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion | 508 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; |
| 490 | lua_Duration const _duration{ lua_tonumber(L_, 2) }; | 509 | if (luaG_type(L_, 2) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion |
| 491 | if (_duration.count() >= 0.0) { | 510 | lua_Duration const _duration{ lua_tonumber(L_, 2) }; |
| 492 | _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); | 511 | if (_duration.count() >= 0.0) { |
| 493 | } else { | 512 | _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); |
| 494 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); | 513 | } else { |
| 495 | } | 514 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); |
| 496 | ++_key_i; | ||
| 497 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | ||
| 498 | ++_key_i; | ||
| 499 | } | ||
| 500 | |||
| 501 | keeper_api_t _selected_keeper_receive{ nullptr }; | ||
| 502 | int _expected_pushed_min{ 0 }, _expected_pushed_max{ 0 }; | ||
| 503 | // are we in batched mode? | ||
| 504 | if (kLindaBatched.equals(L_, _key_i)) { | ||
| 505 | // no need to pass linda.batched in the keeper state | ||
| 506 | ++_key_i; | ||
| 507 | // make sure the keys are of a valid type | ||
| 508 | check_key_types(L_, _key_i, _key_i); | ||
| 509 | // receive multiple values from a single slot | ||
| 510 | _selected_keeper_receive = KEEPER_API(receive_batched); | ||
| 511 | // we expect a user-defined amount of return value | ||
| 512 | _expected_pushed_min = (int) luaL_checkinteger(L_, _key_i + 1); | ||
| 513 | if (_expected_pushed_min < 1) { | ||
| 514 | raise_luaL_argerror(L_, _key_i + 1, "bad min count"); | ||
| 515 | } | ||
| 516 | _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min); | ||
| 517 | // don't forget to count the key in addition to the values | ||
| 518 | ++_expected_pushed_min; | ||
| 519 | ++_expected_pushed_max; | ||
| 520 | if (_expected_pushed_min > _expected_pushed_max) { | ||
| 521 | raise_luaL_argerror(L_, _key_i + 2, "batched min/max error"); | ||
| 522 | } | ||
| 523 | } else { | ||
| 524 | // make sure the keys are of a valid type | ||
| 525 | check_key_types(L_, _key_i, lua_gettop(L_)); | ||
| 526 | // receive a single value, checking multiple slots | ||
| 527 | _selected_keeper_receive = KEEPER_API(receive); | ||
| 528 | // we expect a single (value, key) pair of returned values | ||
| 529 | _expected_pushed_min = _expected_pushed_max = 2; | ||
| 530 | } | ||
| 531 | |||
| 532 | Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) }; | ||
| 533 | Keeper* const _keeper{ _linda->whichKeeper() }; | ||
| 534 | KeeperState const _K{ _keeper ? _keeper->K : nullptr }; | ||
| 535 | if (_K == nullptr) | ||
| 536 | return 0; | ||
| 537 | |||
| 538 | CancelRequest _cancel{ CancelRequest::None }; | ||
| 539 | KeeperCallResult _pushed{}; | ||
| 540 | STACK_CHECK_START_REL(_K, 0); | ||
| 541 | for (bool _try_again{ true };;) { | ||
| 542 | if (_lane != nullptr) { | ||
| 543 | _cancel = _lane->cancelRequest; | ||
| 544 | } | ||
| 545 | _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest; | ||
| 546 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
| 547 | if (!_try_again || _cancel != CancelRequest::None) { | ||
| 548 | _pushed.emplace(0); | ||
| 549 | break; | ||
| 550 | } | ||
| 551 | |||
| 552 | // all arguments of receive() but the first are passed to the keeper's receive function | ||
| 553 | _pushed = keeper_call(_K, _selected_keeper_receive, L_, _linda, _key_i); | ||
| 554 | if (!_pushed.has_value()) { | ||
| 555 | break; | ||
| 556 | } | ||
| 557 | if (_pushed.value() > 0) { | ||
| 558 | LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max); | ||
| 559 | _linda->readHappened.notify_all(); | ||
| 560 | break; | ||
| 561 | } | ||
| 562 | |||
| 563 | if (std::chrono::steady_clock::now() >= _until) { | ||
| 564 | break; /* instant timeout */ | ||
| 565 | } | ||
| 566 | |||
| 567 | // nothing received, wait until timeout or signalled that we should try again | ||
| 568 | { | ||
| 569 | Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings | ||
| 570 | if (_lane != nullptr) { | ||
| 571 | // change status of lane to "waiting" | ||
| 572 | _prev_status = _lane->status; // Running, most likely | ||
| 573 | LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case | ||
| 574 | _lane->status = Lane::Waiting; | ||
| 575 | LUA_ASSERT(L_, _lane->waiting_on == nullptr); | ||
| 576 | _lane->waiting_on = &_linda->writeHappened; | ||
| 577 | } | ||
| 578 | // not enough data to read: wakeup when data was sent, or when timeout is reached | ||
| 579 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; | ||
| 580 | std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until) }; | ||
| 581 | _guard.release(); // we don't want to unlock the mutex on exit! | ||
| 582 | _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups | ||
| 583 | if (_lane != nullptr) { | ||
| 584 | _lane->waiting_on = nullptr; | ||
| 585 | _lane->status = _prev_status; | ||
| 586 | } | 515 | } |
| 516 | ++_key_i; | ||
| 517 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | ||
| 518 | ++_key_i; | ||
| 587 | } | 519 | } |
| 588 | } | ||
| 589 | STACK_CHECK(_K, 0); | ||
| 590 | |||
| 591 | if (!_pushed.has_value()) { | ||
| 592 | raise_luaL_error(L_, "tried to copy unsupported types"); | ||
| 593 | } | ||
| 594 | 520 | ||
| 595 | switch (_cancel) { | 521 | keeper_api_t _selected_keeper_receive{ nullptr }; |
| 596 | case CancelRequest::None: | 522 | int _expected_pushed_min{ 0 }, _expected_pushed_max{ 0 }; |
| 597 | { | 523 | // are we in batched mode? |
| 598 | int const _nbPushed{ _pushed.value() }; | 524 | if (kLindaBatched.equals(L_, _key_i)) { |
| 599 | if (_nbPushed == 0) { | 525 | // no need to pass linda.batched in the keeper state |
| 600 | // not enough data in the linda slot to fulfill the request, return nil, "timeout" | 526 | ++_key_i; |
| 601 | lua_pushnil(L_); | 527 | // make sure the keys are of a valid type |
| 602 | luaG_pushstring(L_, "timeout"); | 528 | CheckKeyTypes(L_, _key_i, _key_i); |
| 603 | return 2; | 529 | // receive multiple values from a single slot |
| 530 | _selected_keeper_receive = KEEPER_API(receive_batched); | ||
| 531 | // we expect a user-defined amount of return value | ||
| 532 | _expected_pushed_min = (int) luaL_checkinteger(L_, _key_i + 1); | ||
| 533 | if (_expected_pushed_min < 1) { | ||
| 534 | raise_luaL_argerror(L_, _key_i + 1, "bad min count"); | ||
| 535 | } | ||
| 536 | _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min); | ||
| 537 | // don't forget to count the key in addition to the values | ||
| 538 | ++_expected_pushed_min; | ||
| 539 | ++_expected_pushed_max; | ||
| 540 | if (_expected_pushed_min > _expected_pushed_max) { | ||
| 541 | raise_luaL_argerror(L_, _key_i + 2, "batched min/max error"); | ||
| 604 | } | 542 | } |
| 605 | return _nbPushed; | ||
| 606 | } | ||
| 607 | |||
| 608 | case CancelRequest::Soft: | ||
| 609 | // if user wants to soft-cancel, the call returns nil, kCancelError | ||
| 610 | lua_pushnil(L_); | ||
| 611 | kCancelError.pushKey(L_); | ||
| 612 | return 2; | ||
| 613 | |||
| 614 | case CancelRequest::Hard: | ||
| 615 | // raise an error interrupting execution only in case of hard cancel | ||
| 616 | raise_cancel_error(L_); // raises an error and doesn't return | ||
| 617 | |||
| 618 | default: | ||
| 619 | raise_luaL_error(L_, "internal error: unknown cancel request"); | ||
| 620 | } | ||
| 621 | }; | ||
| 622 | return Linda::ProtectedCall(L_, _receive); | ||
| 623 | } | ||
| 624 | |||
| 625 | // ################################################################################################# | ||
| 626 | |||
| 627 | /* | ||
| 628 | * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) | ||
| 629 | * | ||
| 630 | * Send one or more values to a Linda. If there is a limit, all values must fit. | ||
| 631 | * | ||
| 632 | * Returns: 'true' if the value was queued | ||
| 633 | * 'false' for timeout (only happens when the queue size is limited) | ||
| 634 | * nil, kCancelError if cancelled | ||
| 635 | */ | ||
| 636 | LUAG_FUNC(linda_send) | ||
| 637 | { | ||
| 638 | auto _send = [](lua_State* L_) { | ||
| 639 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | ||
| 640 | int _key_i{ 2 }; // index of first key, if timeout not there | ||
| 641 | |||
| 642 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
| 643 | if (luaG_type(L_, 2) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion | ||
| 644 | lua_Duration const _duration{ lua_tonumber(L_, 2) }; | ||
| 645 | if (_duration.count() >= 0.0) { | ||
| 646 | _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); | ||
| 647 | } else { | 543 | } else { |
| 648 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); | 544 | // make sure the keys are of a valid type |
| 545 | CheckKeyTypes(L_, _key_i, lua_gettop(L_)); | ||
| 546 | // receive a single value, checking multiple slots | ||
| 547 | _selected_keeper_receive = KEEPER_API(receive); | ||
| 548 | // we expect a single (value, key) pair of returned values | ||
| 549 | _expected_pushed_min = _expected_pushed_max = 2; | ||
| 649 | } | 550 | } |
| 650 | ++_key_i; | ||
| 651 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | ||
| 652 | ++_key_i; | ||
| 653 | } | ||
| 654 | |||
| 655 | // make sure the key is of a valid type | ||
| 656 | check_key_types(L_, _key_i, _key_i); | ||
| 657 | |||
| 658 | STACK_GROW(L_, 1); | ||
| 659 | 551 | ||
| 660 | // make sure there is something to send | ||
| 661 | if (lua_gettop(L_) == _key_i) { | ||
| 662 | raise_luaL_error(L_, "no data to send"); | ||
| 663 | } | ||
| 664 | |||
| 665 | bool _ret{ false }; | ||
| 666 | CancelRequest _cancel{ CancelRequest::None }; | ||
| 667 | KeeperCallResult _pushed; | ||
| 668 | { | ||
| 669 | Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) }; | 552 | Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) }; |
| 670 | Keeper* const _keeper{ _linda->whichKeeper() }; | 553 | Keeper* const _keeper{ _linda->whichKeeper() }; |
| 671 | KeeperState const _K{ _keeper ? _keeper->K : nullptr }; | 554 | KeeperState const _K{ _keeper ? _keeper->K : nullptr }; |
| 672 | if (_K == nullptr) | 555 | if (_K == nullptr) |
| 673 | return 0; | 556 | return 0; |
| 674 | 557 | ||
| 558 | CancelRequest _cancel{ CancelRequest::None }; | ||
| 559 | KeeperCallResult _pushed{}; | ||
| 675 | STACK_CHECK_START_REL(_K, 0); | 560 | STACK_CHECK_START_REL(_K, 0); |
| 676 | for (bool _try_again{ true };;) { | 561 | for (bool _try_again{ true };;) { |
| 677 | if (_lane != nullptr) { | 562 | if (_lane != nullptr) { |
| @@ -684,28 +569,22 @@ LUAG_FUNC(linda_send) | |||
| 684 | break; | 569 | break; |
| 685 | } | 570 | } |
| 686 | 571 | ||
| 687 | STACK_CHECK(_K, 0); | 572 | // all arguments of receive() but the first are passed to the keeper's receive function |
| 688 | _pushed = keeper_call(_K, KEEPER_API(send), L_, _linda, _key_i); | 573 | _pushed = keeper_call(_K, _selected_keeper_receive, L_, _linda, _key_i); |
| 689 | if (!_pushed.has_value()) { | 574 | if (!_pushed.has_value()) { |
| 690 | break; | 575 | break; |
| 691 | } | 576 | } |
| 692 | LUA_ASSERT(L_, _pushed.value() == 1); | 577 | if (_pushed.value() > 0) { |
| 693 | 578 | LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max); | |
| 694 | _ret = lua_toboolean(L_, -1) ? true : false; | 579 | _linda->readHappened.notify_all(); |
| 695 | lua_pop(L_, 1); | ||
| 696 | |||
| 697 | if (_ret) { | ||
| 698 | // Wake up ALL waiting threads | ||
| 699 | _linda->writeHappened.notify_all(); | ||
| 700 | break; | 580 | break; |
| 701 | } | 581 | } |
| 702 | 582 | ||
| 703 | // instant timout to bypass the wait syscall | ||
| 704 | if (std::chrono::steady_clock::now() >= _until) { | 583 | if (std::chrono::steady_clock::now() >= _until) { |
| 705 | break; /* no wait; instant timeout */ | 584 | break; /* instant timeout */ |
| 706 | } | 585 | } |
| 707 | 586 | ||
| 708 | // storage limit hit, wait until timeout or signalled that we should try again | 587 | // nothing received, wait until timeout or signalled that we should try again |
| 709 | { | 588 | { |
| 710 | Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings | 589 | Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings |
| 711 | if (_lane != nullptr) { | 590 | if (_lane != nullptr) { |
| @@ -714,13 +593,13 @@ LUAG_FUNC(linda_send) | |||
| 714 | LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case | 593 | LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case |
| 715 | _lane->status = Lane::Waiting; | 594 | _lane->status = Lane::Waiting; |
| 716 | LUA_ASSERT(L_, _lane->waiting_on == nullptr); | 595 | LUA_ASSERT(L_, _lane->waiting_on == nullptr); |
| 717 | _lane->waiting_on = &_linda->readHappened; | 596 | _lane->waiting_on = &_linda->writeHappened; |
| 718 | } | 597 | } |
| 719 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | 598 | // not enough data to read: wakeup when data was sent, or when timeout is reached |
| 720 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; | 599 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; |
| 721 | std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until) }; | 600 | std::cv_status const _status{ _linda->writeHappened.wait_until(_guard, _until) }; |
| 722 | _guard.release(); // we don't want to unlock the mutex on exit! | 601 | _guard.release(); // we don't want to unlock the mutex on exit! |
| 723 | _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups | 602 | _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups |
| 724 | if (_lane != nullptr) { | 603 | if (_lane != nullptr) { |
| 725 | _lane->waiting_on = nullptr; | 604 | _lane->waiting_on = nullptr; |
| 726 | _lane->status = _prev_status; | 605 | _lane->status = _prev_status; |
| @@ -728,32 +607,176 @@ LUAG_FUNC(linda_send) | |||
| 728 | } | 607 | } |
| 729 | } | 608 | } |
| 730 | STACK_CHECK(_K, 0); | 609 | STACK_CHECK(_K, 0); |
| 731 | } | ||
| 732 | 610 | ||
| 733 | if (!_pushed.has_value()) { | 611 | if (!_pushed.has_value()) { |
| 734 | raise_luaL_error(L_, "tried to copy unsupported types"); | 612 | raise_luaL_error(L_, "tried to copy unsupported types"); |
| 613 | } | ||
| 614 | |||
| 615 | switch (_cancel) { | ||
| 616 | case CancelRequest::None: | ||
| 617 | { | ||
| 618 | int const _nbPushed{ _pushed.value() }; | ||
| 619 | if (_nbPushed == 0) { | ||
| 620 | // not enough data in the linda slot to fulfill the request, return nil, "timeout" | ||
| 621 | lua_pushnil(L_); | ||
| 622 | luaG_pushstring(L_, "timeout"); | ||
| 623 | return 2; | ||
| 624 | } | ||
| 625 | return _nbPushed; | ||
| 626 | } | ||
| 627 | |||
| 628 | case CancelRequest::Soft: | ||
| 629 | // if user wants to soft-cancel, the call returns nil, kCancelError | ||
| 630 | lua_pushnil(L_); | ||
| 631 | kCancelError.pushKey(L_); | ||
| 632 | return 2; | ||
| 633 | |||
| 634 | case CancelRequest::Hard: | ||
| 635 | // raise an error interrupting execution only in case of hard cancel | ||
| 636 | raise_cancel_error(L_); // raises an error and doesn't return | ||
| 637 | |||
| 638 | default: | ||
| 639 | raise_luaL_error(L_, "internal error: unknown cancel request"); | ||
| 640 | } | ||
| 735 | } | 641 | } |
| 642 | }; | ||
| 643 | return Linda::ProtectedCall(L_, _receive); | ||
| 644 | } | ||
| 645 | |||
| 646 | // ################################################################################################# | ||
| 647 | |||
| 648 | /* | ||
| 649 | * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) | ||
| 650 | * | ||
| 651 | * Send one or more values to a Linda. If there is a limit, all values must fit. | ||
| 652 | * | ||
| 653 | * Returns: 'true' if the value was queued | ||
| 654 | * 'false' for timeout (only happens when the queue size is limited) | ||
| 655 | * nil, kCancelError if cancelled | ||
| 656 | */ | ||
| 657 | LUAG_FUNC(linda_send) | ||
| 658 | { | ||
| 659 | static constexpr lua_CFunction _send{ | ||
| 660 | +[](lua_State* const L_) { | ||
| 661 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | ||
| 662 | int _key_i{ 2 }; // index of first key, if timeout not there | ||
| 663 | |||
| 664 | std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; | ||
| 665 | if (luaG_type(L_, 2) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion | ||
| 666 | lua_Duration const _duration{ lua_tonumber(L_, 2) }; | ||
| 667 | if (_duration.count() >= 0.0) { | ||
| 668 | _until = std::chrono::steady_clock::now() + std::chrono::duration_cast<std::chrono::steady_clock::duration>(_duration); | ||
| 669 | } else { | ||
| 670 | raise_luaL_argerror(L_, 2, "duration cannot be < 0"); | ||
| 671 | } | ||
| 672 | ++_key_i; | ||
| 673 | } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key | ||
| 674 | ++_key_i; | ||
| 675 | } | ||
| 736 | 676 | ||
| 737 | switch (_cancel) { | 677 | // make sure the key is of a valid type |
| 738 | case CancelRequest::Soft: | 678 | CheckKeyTypes(L_, _key_i, _key_i); |
| 739 | // if user wants to soft-cancel, the call returns nil, kCancelError | ||
| 740 | lua_pushnil(L_); | ||
| 741 | kCancelError.pushKey(L_); | ||
| 742 | return 2; | ||
| 743 | 679 | ||
| 744 | case CancelRequest::Hard: | 680 | STACK_GROW(L_, 1); |
| 745 | // raise an error interrupting execution only in case of hard cancel | ||
| 746 | raise_cancel_error(L_); // raises an error and doesn't return | ||
| 747 | 681 | ||
| 748 | default: | 682 | // make sure there is something to send |
| 749 | if (_ret) { | 683 | if (lua_gettop(L_) == _key_i) { |
| 750 | lua_pushboolean(L_, _ret); // true (success) | 684 | raise_luaL_error(L_, "no data to send"); |
| 751 | return 1; | 685 | } |
| 752 | } else { | 686 | |
| 753 | // not enough room in the Linda slot to fulfill the request, return nil, "timeout" | 687 | bool _ret{ false }; |
| 688 | CancelRequest _cancel{ CancelRequest::None }; | ||
| 689 | KeeperCallResult _pushed; | ||
| 690 | { | ||
| 691 | Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue<Lane>(L_) }; | ||
| 692 | Keeper* const _keeper{ _linda->whichKeeper() }; | ||
| 693 | KeeperState const _K{ _keeper ? _keeper->K : nullptr }; | ||
| 694 | if (_K == nullptr) | ||
| 695 | return 0; | ||
| 696 | |||
| 697 | STACK_CHECK_START_REL(_K, 0); | ||
| 698 | for (bool _try_again{ true };;) { | ||
| 699 | if (_lane != nullptr) { | ||
| 700 | _cancel = _lane->cancelRequest; | ||
| 701 | } | ||
| 702 | _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest; | ||
| 703 | // if user wants to cancel, or looped because of a timeout, the call returns without sending anything | ||
| 704 | if (!_try_again || _cancel != CancelRequest::None) { | ||
| 705 | _pushed.emplace(0); | ||
| 706 | break; | ||
| 707 | } | ||
| 708 | |||
| 709 | STACK_CHECK(_K, 0); | ||
| 710 | _pushed = keeper_call(_K, KEEPER_API(send), L_, _linda, _key_i); | ||
| 711 | if (!_pushed.has_value()) { | ||
| 712 | break; | ||
| 713 | } | ||
| 714 | LUA_ASSERT(L_, _pushed.value() == 1); | ||
| 715 | |||
| 716 | _ret = lua_toboolean(L_, -1) ? true : false; | ||
| 717 | lua_pop(L_, 1); | ||
| 718 | |||
| 719 | if (_ret) { | ||
| 720 | // Wake up ALL waiting threads | ||
| 721 | _linda->writeHappened.notify_all(); | ||
| 722 | break; | ||
| 723 | } | ||
| 724 | |||
| 725 | // instant timout to bypass the wait syscall | ||
| 726 | if (std::chrono::steady_clock::now() >= _until) { | ||
| 727 | break; /* no wait; instant timeout */ | ||
| 728 | } | ||
| 729 | |||
| 730 | // storage limit hit, wait until timeout or signalled that we should try again | ||
| 731 | { | ||
| 732 | Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings | ||
| 733 | if (_lane != nullptr) { | ||
| 734 | // change status of lane to "waiting" | ||
| 735 | _prev_status = _lane->status; // Running, most likely | ||
| 736 | LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case | ||
| 737 | _lane->status = Lane::Waiting; | ||
| 738 | LUA_ASSERT(L_, _lane->waiting_on == nullptr); | ||
| 739 | _lane->waiting_on = &_linda->readHappened; | ||
| 740 | } | ||
| 741 | // could not send because no room: wait until some data was read before trying again, or until timeout is reached | ||
| 742 | std::unique_lock<std::mutex> _guard{ _keeper->mutex, std::adopt_lock }; | ||
| 743 | std::cv_status const status{ _linda->readHappened.wait_until(_guard, _until) }; | ||
| 744 | _guard.release(); // we don't want to unlock the mutex on exit! | ||
| 745 | _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups | ||
| 746 | if (_lane != nullptr) { | ||
| 747 | _lane->waiting_on = nullptr; | ||
| 748 | _lane->status = _prev_status; | ||
| 749 | } | ||
| 750 | } | ||
| 751 | } | ||
| 752 | STACK_CHECK(_K, 0); | ||
| 753 | } | ||
| 754 | |||
| 755 | if (!_pushed.has_value()) { | ||
| 756 | raise_luaL_error(L_, "tried to copy unsupported types"); | ||
| 757 | } | ||
| 758 | |||
| 759 | switch (_cancel) { | ||
| 760 | case CancelRequest::Soft: | ||
| 761 | // if user wants to soft-cancel, the call returns nil, kCancelError | ||
| 754 | lua_pushnil(L_); | 762 | lua_pushnil(L_); |
| 755 | luaG_pushstring(L_, "timeout"); | 763 | kCancelError.pushKey(L_); |
| 756 | return 2; | 764 | return 2; |
| 765 | |||
| 766 | case CancelRequest::Hard: | ||
| 767 | // raise an error interrupting execution only in case of hard cancel | ||
| 768 | raise_cancel_error(L_); // raises an error and doesn't return | ||
| 769 | |||
| 770 | default: | ||
| 771 | if (_ret) { | ||
| 772 | lua_pushboolean(L_, _ret); // true (success) | ||
| 773 | return 1; | ||
| 774 | } else { | ||
| 775 | // not enough room in the Linda slot to fulfill the request, return nil, "timeout" | ||
| 776 | lua_pushnil(L_); | ||
| 777 | luaG_pushstring(L_, "timeout"); | ||
| 778 | return 2; | ||
| 779 | } | ||
| 757 | } | 780 | } |
| 758 | } | 781 | } |
| 759 | }; | 782 | }; |
| @@ -771,39 +794,41 @@ LUAG_FUNC(linda_send) | |||
| 771 | */ | 794 | */ |
| 772 | LUAG_FUNC(linda_set) | 795 | LUAG_FUNC(linda_set) |
| 773 | { | 796 | { |
| 774 | auto set = [](lua_State* L_) { | 797 | static constexpr lua_CFunction _set{ |
| 775 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 798 | +[](lua_State* const L_) { |
| 776 | bool const _has_data{ lua_gettop(L_) > 2 }; | 799 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
| 777 | // make sure the key is of a valid type (throws an error if not the case) | 800 | bool const _has_data{ lua_gettop(L_) > 2 }; |
| 778 | check_key_types(L_, 2, 2); | 801 | // make sure the key is of a valid type (throws an error if not the case) |
| 779 | 802 | CheckKeyTypes(L_, 2, 2); | |
| 780 | KeeperCallResult _pushed; | 803 | |
| 781 | if (_linda->cancelRequest == CancelRequest::None) { | 804 | KeeperCallResult _pushed; |
| 782 | Keeper* const _keeper{ _linda->whichKeeper() }; | 805 | if (_linda->cancelRequest == CancelRequest::None) { |
| 783 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, 2); | 806 | Keeper* const _keeper{ _linda->whichKeeper() }; |
| 784 | if (_pushed.has_value()) { // no error? | 807 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, 2); |
| 785 | LUA_ASSERT(L_, _pushed.value() == 1 && luaG_type(L_, -1) == LuaType::BOOLEAN); | 808 | if (_pushed.has_value()) { // no error? |
| 786 | 809 | LUA_ASSERT(L_, _pushed.value() == 1 && luaG_type(L_, -1) == LuaType::BOOLEAN); | |
| 787 | if (_has_data) { | 810 | |
| 788 | // we put some data in the slot, tell readers that they should wake | 811 | if (_has_data) { |
| 789 | _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area | 812 | // we put some data in the slot, tell readers that they should wake |
| 790 | } | 813 | _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area |
| 791 | if (lua_toboolean(L_, -1)) { | 814 | } |
| 792 | // the key was full, but it is no longer the case, tell writers they should wake | 815 | if (lua_toboolean(L_, -1)) { |
| 793 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | 816 | // the key was full, but it is no longer the case, tell writers they should wake |
| 817 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | ||
| 818 | } | ||
| 794 | } | 819 | } |
| 820 | } else { // linda is cancelled | ||
| 821 | // do nothing and return nil,lanes.cancel_error | ||
| 822 | lua_pushnil(L_); | ||
| 823 | kCancelError.pushKey(L_); | ||
| 824 | _pushed.emplace(2); | ||
| 795 | } | 825 | } |
| 796 | } else { // linda is cancelled | ||
| 797 | // do nothing and return nil,lanes.cancel_error | ||
| 798 | lua_pushnil(L_); | ||
| 799 | kCancelError.pushKey(L_); | ||
| 800 | _pushed.emplace(2); | ||
| 801 | } | ||
| 802 | 826 | ||
| 803 | // must trigger any error after keeper state has been released | 827 | // must trigger any error after keeper state has been released |
| 804 | return OptionalValue(_pushed, L_, "tried to copy unsupported types"); | 828 | return OptionalValue(_pushed, L_, "tried to copy unsupported types"); |
| 829 | } | ||
| 805 | }; | 830 | }; |
| 806 | return Linda::ProtectedCall(L_, set); | 831 | return Linda::ProtectedCall(L_, _set); |
| 807 | } | 832 | } |
| 808 | 833 | ||
| 809 | // ################################################################################################# | 834 | // ################################################################################################# |
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp index 9ae2611..d5ebc9b 100644 --- a/src/lindafactory.cpp +++ b/src/lindafactory.cpp | |||
| @@ -35,8 +35,7 @@ THE SOFTWARE. | |||
| 35 | 35 | ||
| 36 | #include "linda.h" | 36 | #include "linda.h" |
| 37 | 37 | ||
| 38 | // must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it) | 38 | static constexpr std::string_view kLindaMetatableName{ "Linda" }; |
| 39 | #define kLindaMetatableName "Linda" | ||
| 40 | 39 | ||
| 41 | // ################################################################################################# | 40 | // ################################################################################################# |
| 42 | 41 | ||
diff --git a/src/nameof.cpp b/src/nameof.cpp index a33c2e5..3c82603 100644 --- a/src/nameof.cpp +++ b/src/nameof.cpp | |||
| @@ -206,4 +206,3 @@ LUAG_FUNC(nameof) | |||
| 206 | lua_replace(L_, -3); // L_: "type" "result" | 206 | lua_replace(L_, -3); // L_: "type" "result" |
| 207 | return 2; | 207 | return 2; |
| 208 | } | 208 | } |
| 209 | |||
diff --git a/src/state.cpp b/src/state.cpp index 267554e..3f6b3d7 100644 --- a/src/state.cpp +++ b/src/state.cpp | |||
| @@ -50,7 +50,7 @@ namespace { | |||
| 50 | // ############################################################################################# | 50 | // ############################################################################################# |
| 51 | // ############################################################################################# | 51 | // ############################################################################################# |
| 52 | 52 | ||
| 53 | [[nodiscard]] static int require_lanes_core(lua_State* L_) | 53 | [[nodiscard]] static int require_lanes_core(lua_State* const L_) |
| 54 | { | 54 | { |
| 55 | // leaves a copy of 'lanes.core' module table on the stack | 55 | // leaves a copy of 'lanes.core' module table on the stack |
| 56 | luaL_requiref(L_, kLanesCoreLibName, luaopen_lanes_core, 0); | 56 | luaL_requiref(L_, kLanesCoreLibName, luaopen_lanes_core, 0); |
| @@ -95,7 +95,7 @@ namespace { | |||
| 95 | 95 | ||
| 96 | // ############################################################################################# | 96 | // ############################################################################################# |
| 97 | 97 | ||
| 98 | static void Open1Lib(lua_State* L_, std::string_view const& name_) | 98 | static void Open1Lib(lua_State* const L_, std::string_view const& name_) |
| 99 | { | 99 | { |
| 100 | for (luaL_Reg const& _entry : local::sLibs) { | 100 | for (luaL_Reg const& _entry : local::sLibs) { |
| 101 | if (name_ == _entry.name) { | 101 | if (name_ == _entry.name) { |
| @@ -123,7 +123,7 @@ namespace { | |||
| 123 | // ############################################################################################# | 123 | // ############################################################################################# |
| 124 | 124 | ||
| 125 | // just like lua_xmove, args are (from, to) | 125 | // just like lua_xmove, args are (from, to) |
| 126 | static void CopyOneTimeSettings(Universe* U_, SourceState L1_, DestState L2_) | 126 | static void CopyOneTimeSettings(Universe* const U_, SourceState const L1_, DestState const L2_) |
| 127 | { | 127 | { |
| 128 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ }); | 128 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ }); |
| 129 | 129 | ||
| @@ -157,7 +157,7 @@ namespace state { | |||
| 157 | // ############################################################################################# | 157 | // ############################################################################################# |
| 158 | // ############################################################################################# | 158 | // ############################################################################################# |
| 159 | 159 | ||
| 160 | void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_) | 160 | void CallOnStateCreate(Universe* const U_, lua_State* const L_, lua_State* const from_, LookupMode const mode_) |
| 161 | { | 161 | { |
| 162 | if (U_->onStateCreateFunc == nullptr) { | 162 | if (U_->onStateCreateFunc == nullptr) { |
| 163 | return; | 163 | return; |
| @@ -195,7 +195,7 @@ namespace state { | |||
| 195 | 195 | ||
| 196 | // ############################################################################################# | 196 | // ############################################################################################# |
| 197 | 197 | ||
| 198 | lua_State* CreateState([[maybe_unused]] Universe* U_, lua_State* from_) | 198 | lua_State* CreateState([[maybe_unused]] Universe* const U_, lua_State* const from_) |
| 199 | { | 199 | { |
| 200 | lua_State* const _L { | 200 | lua_State* const _L { |
| 201 | std::invoke( | 201 | std::invoke( |
| @@ -228,7 +228,7 @@ namespace state { | |||
| 228 | 228 | ||
| 229 | // ############################################################################################# | 229 | // ############################################################################################# |
| 230 | 230 | ||
| 231 | void InitializeOnStateCreate(Universe* U_, lua_State* L_) | 231 | void InitializeOnStateCreate(Universe* const U_, lua_State* const L_) |
| 232 | { | 232 | { |
| 233 | STACK_CHECK_START_REL(L_, 1); // L_: settings | 233 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
| 234 | if (luaG_getfield(L_, -1, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil | 234 | if (luaG_getfield(L_, -1, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil |
| @@ -266,7 +266,7 @@ namespace state { | |||
| 266 | * Base ("unpack", "print" etc.) is always added, unless 'libs' is nullptr. | 266 | * Base ("unpack", "print" etc.) is always added, unless 'libs' is nullptr. |
| 267 | * | 267 | * |
| 268 | */ | 268 | */ |
| 269 | lua_State* NewLaneState(Universe* U_, SourceState from_, std::optional<std::string_view> const& libs_) | 269 | lua_State* NewLaneState(Universe* const U_, SourceState const from_, std::optional<std::string_view> const& libs_) |
| 270 | { | 270 | { |
| 271 | DestState const _L{ CreateState(U_, from_) }; | 271 | DestState const _L{ CreateState(U_, from_) }; |
| 272 | 272 | ||
diff --git a/src/tools.cpp b/src/tools.cpp index 9fc1e35..14786b2 100644 --- a/src/tools.cpp +++ b/src/tools.cpp | |||
| @@ -65,15 +65,15 @@ static constexpr int kWriterReturnCode{ 666 }; | |||
| 65 | * +-----------------+-------------------+------------+----------+ | 65 | * +-----------------+-------------------+------------+----------+ |
| 66 | */ | 66 | */ |
| 67 | 67 | ||
| 68 | [[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | 68 | FuncSubType luaG_getfuncsubtype(lua_State* const L_, int const i_) |
| 69 | { | 69 | { |
| 70 | if (lua_tocfunction(L_, _i)) { // nullptr for LuaJIT-fast && bytecode functions | 70 | if (lua_tocfunction(L_, i_)) { // nullptr for LuaJIT-fast && bytecode functions |
| 71 | return FuncSubType::Native; | 71 | return FuncSubType::Native; |
| 72 | } | 72 | } |
| 73 | { | 73 | { |
| 74 | int _mustpush{ 0 }; | 74 | int _mustpush{ 0 }; |
| 75 | if (luaG_absindex(L_, _i) != lua_gettop(L_)) { | 75 | if (luaG_absindex(L_, i_) != lua_gettop(L_)) { |
| 76 | lua_pushvalue(L_, _i); | 76 | lua_pushvalue(L_, i_); |
| 77 | _mustpush = 1; | 77 | _mustpush = 1; |
| 78 | } | 78 | } |
| 79 | // the provided writer fails with code kWriterReturnCode | 79 | // the provided writer fails with code kWriterReturnCode |
| @@ -93,11 +93,11 @@ static constexpr int kWriterReturnCode{ 666 }; | |||
| 93 | namespace tools { | 93 | namespace tools { |
| 94 | 94 | ||
| 95 | // inspired from tconcat() in ltablib.c | 95 | // inspired from tconcat() in ltablib.c |
| 96 | [[nodiscard]] std::string_view PushFQN(lua_State* L_, int t_, int last_) | 96 | std::string_view PushFQN(lua_State* const L_, int const t_, int const last_) |
| 97 | { | 97 | { |
| 98 | luaL_Buffer _b; | ||
| 99 | STACK_CHECK_START_REL(L_, 0); | 98 | STACK_CHECK_START_REL(L_, 0); |
| 100 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... | 99 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... |
| 100 | luaL_Buffer _b; | ||
| 101 | luaL_buffinit(L_, &_b); // L_: ... {} ... &b? | 101 | luaL_buffinit(L_, &_b); // L_: ... {} ... &b? |
| 102 | int _i{ 1 }; | 102 | int _i{ 1 }; |
| 103 | for (; _i < last_; ++_i) { | 103 | for (; _i < last_; ++_i) { |
diff --git a/src/tools.h b/src/tools.h index e240fdb..5127ea0 100644 --- a/src/tools.h +++ b/src/tools.h | |||
| @@ -18,7 +18,7 @@ enum class FuncSubType | |||
| 18 | FastJIT | 18 | FastJIT |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | [[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i); | 21 | [[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int i_); |
| 22 | 22 | ||
| 23 | // ################################################################################################# | 23 | // ################################################################################################# |
| 24 | 24 | ||
