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