aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-12-03 10:26:47 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-12-03 10:26:47 +0100
commit307fd830eb168005a3ba3d557343284814757eff (patch)
treeeb14512492d58e33585bc5df3f3d9b23d7308934 /src
parentca7657e24549acb8a2dd45fa81c309b5bf9f61ee (diff)
downloadlanes-307fd830eb168005a3ba3d557343284814757eff.tar.gz
lanes-307fd830eb168005a3ba3d557343284814757eff.tar.bz2
lanes-307fd830eb168005a3ba3d557343284814757eff.zip
New method linda:restrict()
Diffstat (limited to 'src')
-rw-r--r--src/cancel.cpp2
-rw-r--r--src/compat.hpp10
-rw-r--r--src/keeper.cpp145
-rw-r--r--src/keeper.hpp14
-rw-r--r--src/lanes.lua1
-rw-r--r--src/linda.cpp104
6 files changed, 239 insertions, 37 deletions
diff --git a/src/cancel.cpp b/src/cancel.cpp
index 8fa68d5..31656c4 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -164,7 +164,7 @@ LUAG_FUNC(lane_cancel)
164 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); 164 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0");
165 } 165 }
166 lua_remove(L_, 2); // argument is processed, remove it 166 lua_remove(L_, 2); // argument is processed, remove it
167 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key 167 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil
168 lua_remove(L_, 2); // argument is processed, remove it 168 lua_remove(L_, 2); // argument is processed, remove it
169 } 169 }
170 170
diff --git a/src/compat.hpp b/src/compat.hpp
index f66f703..81f1665 100644
--- a/src/compat.hpp
+++ b/src/compat.hpp
@@ -272,6 +272,16 @@ LuaType luaG_getmodule(lua_State* L_, std::string_view const& name_);
272 272
273// ################################################################################################# 273// #################################################################################################
274 274
275template<typename ENUM>
276requires std::is_enum_v<ENUM>
277[[nodiscard]]
278ENUM luaG_optenum(lua_State* const L_, StackIndex const idx_, ENUM const def_)
279{
280 return static_cast<ENUM>(luaL_optinteger(L_, idx_, static_cast<std::underlying_type_t<ENUM>>(def_)));
281}
282
283// #################################################################################################
284
275inline void luaG_registerlibfuncs(lua_State* L_, luaL_Reg const* funcs_) 285inline void luaG_registerlibfuncs(lua_State* L_, luaL_Reg const* funcs_)
276{ 286{
277 // fake externs to make clang happy... 287 // fake externs to make clang happy...
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 8a99a36..e7b02e6 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -73,6 +73,7 @@ class KeyUD
73 int first{ 1 }; 73 int first{ 1 };
74 int count{ 0 }; 74 int count{ 0 };
75 LindaLimit limit{ -1 }; 75 LindaLimit limit{ -1 };
76 LindaRestrict restrict { LindaRestrict::None };
76 77
77 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents 78 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
78 [[nodiscard]] 79 [[nodiscard]]
@@ -84,6 +85,8 @@ class KeyUD
84 [[nodiscard]] 85 [[nodiscard]]
85 bool changeLimit(LindaLimit limit_); 86 bool changeLimit(LindaLimit limit_);
86 [[nodiscard]] 87 [[nodiscard]]
88 LindaRestrict changeRestrict(LindaRestrict restrict_);
89 [[nodiscard]]
87 static KeyUD* Create(KeeperState K_); 90 static KeyUD* Create(KeeperState K_);
88 [[nodiscard]] 91 [[nodiscard]]
89 static KeyUD* GetPtr(KeeperState K_, StackIndex idx_); 92 static KeyUD* GetPtr(KeeperState K_, StackIndex idx_);
@@ -114,6 +117,14 @@ bool KeyUD::changeLimit(LindaLimit const limit_)
114 117
115// ################################################################################################# 118// #################################################################################################
116 119
120[[nodiscard]]
121LindaRestrict KeyUD::changeRestrict(LindaRestrict const restrict_)
122{
123 return std::exchange(restrict, restrict_);
124}
125
126// #################################################################################################
127
117// in: nothing 128// in: nothing
118// out: { first = 1, count = 0, limit = -1} 129// out: { first = 1, count = 0, limit = -1}
119KeyUD* KeyUD::Create(KeeperState const K_) 130KeyUD* KeyUD::Create(KeeperState const K_)
@@ -280,7 +291,7 @@ bool KeyUD::reset(KeeperState const K_)
280 LUA_ASSERT(K_, KeyUD::GetPtr(K_, kIdxTop) == this); 291 LUA_ASSERT(K_, KeyUD::GetPtr(K_, kIdxTop) == this);
281 STACK_CHECK_START_REL(K_, 0); 292 STACK_CHECK_START_REL(K_, 0);
282 bool const _wasFull{ (limit > 0) && (count >= limit) }; 293 bool const _wasFull{ (limit > 0) && (count >= limit) };
283 // empty the KeyUD: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 294 // empty the KeyUD: replace uservalue with a virgin table, reset counters, but leave limit and restrict unchanged!
284 // if we have an actual limit, use it to preconfigure the table 295 // if we have an actual limit, use it to preconfigure the table
285 lua_createtable(K_, (limit <= 0) ? 0 : limit.value(), 0); // K_: KeysDB key val... KeyUD {} 296 lua_createtable(K_, (limit <= 0) ? 0 : limit.value(), 0); // K_: KeysDB key val... KeyUD {}
286 lua_setiuservalue(K_, StackIndex{ -2 }, kContentsTableIndex); // K_: KeysDB key val... KeyUD 297 lua_setiuservalue(K_, StackIndex{ -2 }, kContentsTableIndex); // K_: KeysDB key val... KeyUD
@@ -408,7 +419,7 @@ int keepercall_destruct(lua_State* const L_)
408// ################################################################################################# 419// #################################################################################################
409 420
410// in: linda_ud key [count] 421// in: linda_ud key [count]
411// out: bool + at most <count> values 422// out: N <N values>|kRestrictedChannel
412int keepercall_get(lua_State* const L_) 423int keepercall_get(lua_State* const L_)
413{ 424{
414 KeeperState const _K{ L_ }; 425 KeeperState const _K{ L_ };
@@ -423,7 +434,13 @@ int keepercall_get(lua_State* const L_)
423 lua_remove(_K, 1); // _K: KeyUD 434 lua_remove(_K, 1); // _K: KeyUD
424 KeyUD const* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; 435 KeyUD const* const _key{ KeyUD::GetPtr(_K, kIdxTop) };
425 if (_key != nullptr) { 436 if (_key != nullptr) {
426 _key->peek(_K, _count); // _K: N val... 437 if (_key->restrict == LindaRestrict::SendReceive) { // can we use set/get?
438 lua_settop(_K, 0); // _K:
439 kRestrictedChannel.pushKey(_K); // _K: kRestrictedChannel
440 return 1;
441 } else {
442 _key->peek(_K, _count); // _K: N val...
443 }
427 } else { 444 } else {
428 // no fifo was ever registered for this key, or it is empty 445 // no fifo was ever registered for this key, or it is empty
429 lua_pop(_K, 1); // _K: 446 lua_pop(_K, 1); // _K:
@@ -492,6 +509,17 @@ int keepercall_receive(lua_State* const L_)
492 lua_rawget(_K, 1); // _K: KeysDB keys... KeyUD 509 lua_rawget(_K, 1); // _K: KeysDB keys... KeyUD
493 KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; 510 KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) };
494 if (_key != nullptr) { // it's fine to attempt a read on a key that wasn't yet written to 511 if (_key != nullptr) { // it's fine to attempt a read on a key that wasn't yet written to
512 if (_key->restrict == LindaRestrict::SetGet) { // can we use send/receive?
513 kRestrictedChannel.pushKey(_K); // _K: KeysDB keys... key[i] kRestrictedChannel
514 lua_replace(_K, 1); // _K: kRestrictedChannel keys... key[i]
515 lua_settop(_K, _keyIdx); // _K: kRestrictedChannel keys... key[i]
516 if (_keyIdx != 2) {
517 lua_replace(_K, 2); // _K: kRestrictedChannel key[i] keys...
518 lua_settop(_K, 2); // _K: kRestrictedChannel key[i]
519 }
520 lua_insert(_K, 1); // _K: key kRestrictedChannel
521 return 2;
522 }
495 int const _popped{ _key->pop(_K, 1, 1) }; // _K: KeysDB keys... val 523 int const _popped{ _key->pop(_K, 1, 1) }; // _K: KeysDB keys... val
496 if (_popped > 0) { 524 if (_popped > 0) {
497 lua_replace(_K, 1); // _K: val keys... 525 lua_replace(_K, 1); // _K: val keys...
@@ -527,19 +555,89 @@ int keepercall_receive_batched(lua_State* const L_)
527 lua_rawget(_K, 2); // _K: key KeysDB KeyUD 555 lua_rawget(_K, 2); // _K: key KeysDB KeyUD
528 lua_remove(_K, 2); // _K: key KeyUD 556 lua_remove(_K, 2); // _K: key KeyUD
529 KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; 557 KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) };
530 if (_key == nullptr || _key->pop(_K, _min_count, _max_count) == 0) { // _K: [key val...]|crap 558 if (!_key) {
531 // Lua will adjust the stack for us when we return 559 return 0; // Lua will adjust the stack for us when we return
532 return 0; 560 }
561 if (_key->restrict == LindaRestrict::SetGet) { // can we use send/receive?
562 lua_settop(_K, 1); // _K: key
563 kRestrictedChannel.pushKey(_K); // _K: key kRestrictedChannel
564 return 2;
565 }
566 if (_key->pop(_K, _min_count, _max_count) == 0) { // _K: [key val...]|crap
567 return 0; // Lua will adjust the stack for us when we return
568 }
569 // return whatever remains on the stack at that point: the key and the values we pulled from the fifo
570 return lua_gettop(_K);
571}
572
573// #################################################################################################
574
575// in: linda key [mode]
576// out: mode
577int keepercall_restrict(lua_State* const L_)
578{
579 KeeperState const _K{ L_ };
580 STACK_CHECK_START_ABS(_K, lua_gettop(_K));
581 // no restriction to set, means we read and return the current restriction instead
582 bool const _reading{ lua_gettop(_K) == 2 };
583 auto _decodeRestrict = [_K, _reading]() {
584 if (_reading) {
585 return LindaRestrict::None;
586 }
587 std::string_view const _val{ luaG_tostring(_K, StackIndex{ 3 }) };
588 if (_val == "set/get") {
589 return LindaRestrict::SetGet;
590 }
591 if (_val == "send/receive") {
592 return LindaRestrict::SendReceive;
593 }
594 return LindaRestrict::None;
595 };
596 auto _encodeRestrict = [](LindaRestrict const val_) {
597 switch (val_) {
598 default:
599 case LindaRestrict::None:
600 return std::string_view{ "none" };
601 case LindaRestrict::SetGet:
602 return std::string_view{ "set/get" };
603 case LindaRestrict::SendReceive:
604 return std::string_view{ "send/receive" };
605 }
606 };
607 LindaRestrict const _rstrct{ _decodeRestrict() }; // if we read nil because the argument is absent
608 lua_settop(_K, 2); // _K: linda key
609 PushKeysDB(_K, StackIndex{ 1 }); // _K: linda key KeysDB
610 lua_replace(_K, 1); // _K: KeysDB key
611 lua_pushvalue(_K, -1); // _K: KeysDB key key
612 lua_rawget(_K, -3); // _K: KeysDB key KeyUD|nil
613 KeyUD* _key{ KeyUD::GetPtr(_K, kIdxTop) };
614 if (_reading) {
615 // remove any clutter on the stack
616 lua_settop(_K, 0); // _K:
617 auto const _prevRstrct{ _key ? _key->restrict : LindaRestrict::None };
618 // return a single value: the restrict mode of the key
619 luaG_pushstring(_K, _encodeRestrict(_prevRstrct)); // _K: _previous
533 } else { 620 } else {
534 // return whatever remains on the stack at that point: the key and the values we pulled from the fifo 621 if (_key == nullptr) { // _K: KeysDB key nil
535 return lua_gettop(_K); 622 lua_pop(_K, 1); // _K: KeysDB key
623 _key = KeyUD::Create(_K); // _K: KeysDB key KeyUD
624 lua_rawset(_K, -3); // _K: KeysDB
625 }
626 // remove any clutter on the stack
627 lua_settop(_K, 0); // _K:
628 // return true if we decide that blocked threads waiting to write on that key should be awakened
629 // this is the case if we detect the key was full but it is no longer the case
630 LindaRestrict const _previous{ _key->changeRestrict(_rstrct) };
631 luaG_pushstring(_K, _encodeRestrict(_previous)); // _K: _previous
536 } 632 }
633 STACK_CHECK(_K, 1);
634 return 1;
537} 635}
538 636
539// ################################################################################################# 637// #################################################################################################
540 638
541// in: linda, key, ... 639// in: linda, key, ...
542// out: true|false 640// out: true|false|kRestrictedChannel
543int keepercall_send(lua_State* const L_) 641int keepercall_send(lua_State* const L_)
544{ 642{
545 KeeperState const _K{ L_ }; 643 KeeperState const _K{ L_ };
@@ -561,7 +659,11 @@ int keepercall_send(lua_State* const L_)
561 lua_pop(_K, 1); // _K: linda KeyUD val... 659 lua_pop(_K, 1); // _K: linda KeyUD val...
562 STACK_CHECK(_K, 0); 660 STACK_CHECK(_K, 0);
563 KeyUD* const _key{ KeyUD::GetPtr(_K, StackIndex{ 2 }) }; 661 KeyUD* const _key{ KeyUD::GetPtr(_K, StackIndex{ 2 }) };
564 if (_key && _key->push(_K, _n, true)) { // not enough room? 662 if (_key->restrict == LindaRestrict::SetGet) { // can we use send/receive?
663 lua_settop(_K, 0); // _K:
664 kRestrictedChannel.pushKey(_K); // _K: kRestrictedChannel
665 }
666 else if (_key->push(_K, _n, true)) { // not enough room?
565 lua_settop(_K, 0); // _K: 667 lua_settop(_K, 0); // _K:
566 lua_pushboolean(_K, 1); // _K: true 668 lua_pushboolean(_K, 1); // _K: true
567 } else { 669 } else {
@@ -575,7 +677,7 @@ int keepercall_send(lua_State* const L_)
575// ################################################################################################# 677// #################################################################################################
576 678
577// in: linda key [val...] 679// in: linda key [val...]
578// out: true if the linda was full but it's no longer the case, else false 680// out: true if the linda was full but it's no longer the case, else false, or kRestrictedChannel if the key is restricted
579int keepercall_set(lua_State* const L_) 681int keepercall_set(lua_State* const L_)
580{ 682{
581 KeeperState const _K{ L_ }; 683 KeeperState const _K{ L_ };
@@ -588,11 +690,16 @@ int keepercall_set(lua_State* const L_)
588 lua_pushvalue(_K, 2); // _K: KeysDB key val... key 690 lua_pushvalue(_K, 2); // _K: KeysDB key val... key
589 lua_rawget(_K, 1); // _K: KeysDB key val KeyUD|nil 691 lua_rawget(_K, 1); // _K: KeysDB key val KeyUD|nil
590 KeyUD* _key{ KeyUD::GetPtr(_K, kIdxTop) }; 692 KeyUD* _key{ KeyUD::GetPtr(_K, kIdxTop) };
693 if (_key && _key->restrict == LindaRestrict::SendReceive) { // can we use send/receive?
694 lua_settop(_K, 0); // _K:
695 kRestrictedChannel.pushKey(_K); // _K: kRestrictedChannel
696 return 1;
697 }
591 698
592 if (lua_gettop(_K) == 3) { // no value to set // _K: KeysDB key KeyUD|nil 699 if (lua_gettop(_K) == 3) { // no value to set // _K: KeysDB key KeyUD|nil
593 // empty the KeyUD for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 700 // empty the KeyUD for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
594 if (_key != nullptr) { // might be nullptr if we set a nonexistent key to nil // _K: KeysDB key KeyUD 701 if (_key != nullptr) { // might be nullptr if we set a nonexistent key to nil // _K: KeysDB key KeyUD
595 if (_key->limit < 0) { // KeyUD limit value is the default (unlimited): we can totally remove it 702 if (_key->limit < 0 && _key->restrict == LindaRestrict::None) { // KeyUD limit value and restrict mode are the default (unlimited/none): we can totally remove it
596 lua_pop(_K, 1); // _K: KeysDB key 703 lua_pop(_K, 1); // _K: KeysDB key
597 lua_pushnil(_K); // _K: KeysDB key nil 704 lua_pushnil(_K); // _K: KeysDB key nil
598 lua_rawset(_K, -3); // _K: KeysDB 705 lua_rawset(_K, -3); // _K: KeysDB
@@ -777,6 +884,20 @@ int Keeper::PushLindaStorage(Linda& linda_, DestState const L_)
777 } 884 }
778 STACK_CHECK(L_, 5); 885 STACK_CHECK(L_, 5);
779 lua_setfield(L_, -3, "limit"); // _K: KeysDB key L_: out key keyout fifo 886 lua_setfield(L_, -3, "limit"); // _K: KeysDB key L_: out key keyout fifo
887 // keyout.restrict
888 switch (_key->restrict) {
889 case LindaRestrict::None:
890 luaG_pushstring(L_, "none"); // _K: KeysDB key L_: out key keyout fifo restrict
891 break;
892 case LindaRestrict::SetGet:
893 luaG_pushstring(L_, "set/get"); // _K: KeysDB key L_: out key keyout fifo restrict
894 break;
895 case LindaRestrict::SendReceive:
896 luaG_pushstring(L_, "send/receive"); // _K: KeysDB key L_: out key keyout fifo restrict
897 break;
898 }
899 STACK_CHECK(L_, 5);
900 lua_setfield(L_, -3, "restrict"); // _K: KeysDB key L_: out key keyout fifo
780 // keyout.fifo 901 // keyout.fifo
781 lua_setfield(L_, -2, "fifo"); // _K: KeysDB key L_: out key keyout 902 lua_setfield(L_, -2, "fifo"); // _K: KeysDB key L_: out key keyout
782 // out[key] = keyout 903 // out[key] = keyout
diff --git a/src/keeper.hpp b/src/keeper.hpp
index b77f1a9..e2ad445 100644
--- a/src/keeper.hpp
+++ b/src/keeper.hpp
@@ -13,6 +13,15 @@ DECLARE_UNIQUE_TYPE(KeeperIndex, int);
13 13
14// ################################################################################################# 14// #################################################################################################
15 15
16enum class LindaRestrict
17{
18 None,
19 SetGet,
20 SendReceive
21};
22
23// #################################################################################################
24
16struct Keeper 25struct Keeper
17{ 26{
18 std::mutex mutex; 27 std::mutex mutex;
@@ -79,6 +88,9 @@ DECLARE_UNIQUE_TYPE(KeeperCallResult, std::optional<int>);
79// xxh64 of string "kNilSentinel" generated at https://www.pelock.com/products/hash-calculator 88// xxh64 of string "kNilSentinel" generated at https://www.pelock.com/products/hash-calculator
80static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" }; 89static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" };
81 90
91// xxh64 of string "kRestrictedChannel" generated at https://www.pelock.com/products/hash-calculator
92static constexpr UniqueKey kRestrictedChannel{ 0x4C8B879ECDE110F7ull };
93
82using keeper_api_t = lua_CFunction; 94using keeper_api_t = lua_CFunction;
83#define KEEPER_API(_op) keepercall_##_op 95#define KEEPER_API(_op) keepercall_##_op
84 96
@@ -96,6 +108,8 @@ int keepercall_receive(lua_State* L_);
96[[nodiscard]] 108[[nodiscard]]
97int keepercall_receive_batched(lua_State* L_); 109int keepercall_receive_batched(lua_State* L_);
98[[nodiscard]] 110[[nodiscard]]
111int keepercall_restrict(lua_State* L_);
112[[nodiscard]]
99int keepercall_send(lua_State* L_); 113int keepercall_send(lua_State* L_);
100[[nodiscard]] 114[[nodiscard]]
101int keepercall_set(lua_State* L_); 115int keepercall_set(lua_State* L_);
diff --git a/src/lanes.lua b/src/lanes.lua
index dfa959d..8d8f25d 100644
--- a/src/lanes.lua
+++ b/src/lanes.lua
@@ -448,6 +448,7 @@ local configure_timers = function()
448 448
449 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally has 'table' always declared) 449 -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally has 'table' always declared)
450 local first_time_key = "first time" 450 local first_time_key = "first time"
451 timerLinda:restrict(first_time_key, "set/get")
451 local _, _first_time_val = timerLinda:get(first_time_key) 452 local _, _first_time_val = timerLinda:get(first_time_key)
452 local first_time = (_first_time_val == nil) 453 local first_time = (_first_time_val == nil)
453 timerLinda:set(first_time_key, true) 454 timerLinda:set(first_time_key, true)
diff --git a/src/linda.cpp b/src/linda.cpp
index 43d2a91..6ebbc1f 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -57,7 +57,7 @@ namespace {
57 57
58 case LuaType::USERDATA: 58 case LuaType::USERDATA:
59 if (!DeepFactory::IsDeepUserdata(L_, _i)) { 59 if (!DeepFactory::IsDeepUserdata(L_, _i)) {
60 raise_luaL_error(L_, "argument #%d: can't use non-deep userdata as a key", _i); 60 raise_luaL_error(L_, "argument #%d: can't use non-deep userdata as a slot", _i);
61 } 61 }
62 break; 62 break;
63 63
@@ -66,7 +66,7 @@ namespace {
66 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; 66 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel };
67 for (UniqueKey const& _key : kKeysToCheck) { 67 for (UniqueKey const& _key : kKeysToCheck) {
68 if (_key.equals(L_, _i)) { 68 if (_key.equals(L_, _i)) {
69 raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, _key.debugName.data()); 69 raise_luaL_error(L_, "argument #%d: can't use %s as a slot", _i, _key.debugName.data());
70 break; 70 break;
71 } 71 }
72 } 72 }
@@ -74,7 +74,7 @@ namespace {
74 break; 74 break;
75 75
76 default: 76 default:
77 raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i); 77 raise_luaL_error(L_, "argument #%d: invalid slot type (not a boolean, string, number or light userdata)", _i);
78 } 78 }
79 } 79 }
80 STACK_CHECK(L_, 0); 80 STACK_CHECK(L_, 0);
@@ -416,7 +416,7 @@ static LUAG_FUNC(linda_index)
416// ################################################################################################# 416// #################################################################################################
417 417
418/* 418/*
419 * [val] = linda_count( linda_ud, [key [, ...]]) 419 * [val] = linda_count( linda_ud, [slot [, ...]])
420 * 420 *
421 * Get a count of the pending elements in the specified keys 421 * Get a count of the pending elements in the specified keys
422 */ 422 */
@@ -430,7 +430,7 @@ LUAG_FUNC(linda_count)
430 430
431 Keeper* const _keeper{ _linda->whichKeeper() }; 431 Keeper* const _keeper{ _linda->whichKeeper() };
432 KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(count), L_, _linda, StackIndex{ 2 }) }; 432 KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(count), L_, _linda, StackIndex{ 2 }) };
433 return OptionalValue(_pushed, L_, "tried to count an invalid key"); 433 return OptionalValue(_pushed, L_, "Tried to count an invalid slot");
434 } 434 }
435 }; 435 };
436 return Linda::ProtectedCall(L_, _count); 436 return Linda::ProtectedCall(L_, _count);
@@ -487,13 +487,16 @@ LUAG_FUNC(linda_get)
487 lua_Integer const _count{ luaL_optinteger(L_, 3, 1) }; 487 lua_Integer const _count{ luaL_optinteger(L_, 3, 1) };
488 luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1"); 488 luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1");
489 luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments"); 489 luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments");
490 // make sure the key is of a valid type (throws an error if not the case) 490 // make sure the slot is of a valid type (throws an error if not the case)
491 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); 491 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
492 492
493 KeeperCallResult _pushed; 493 KeeperCallResult _pushed;
494 if (_linda->cancelStatus == Linda::Active) { 494 if (_linda->cancelStatus == Linda::Active) {
495 Keeper* const _keeper{ _linda->whichKeeper() }; 495 Keeper* const _keeper{ _linda->whichKeeper() };
496 _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, StackIndex{ 2 }); 496 _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, StackIndex{ 2 });
497 if (_pushed.has_value() && kRestrictedChannel.equals(L_, kIdxTop)) {
498 raise_luaL_error(L_, "Key is restricted");
499 }
497 } else { // linda is cancelled 500 } else { // linda is cancelled
498 // do nothing and return nil,lanes.cancel_error 501 // do nothing and return nil,lanes.cancel_error
499 lua_pushnil(L_); 502 lua_pushnil(L_);
@@ -511,7 +514,7 @@ LUAG_FUNC(linda_get)
511 514
512/* 515/*
513 * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int]) 516 * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int])
514 * "unlimited"|number = linda:limit(key) 517 * "unlimited"|number = linda:limit(slot)
515 * 518 *
516 * Read or set limit to 1 Linda keys. 519 * Read or set limit to 1 Linda keys.
517 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so 520 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so
@@ -522,7 +525,7 @@ LUAG_FUNC(linda_limit)
522 static constexpr lua_CFunction _limit{ 525 static constexpr lua_CFunction _limit{
523 +[](lua_State* const L_) { 526 +[](lua_State* const L_) {
524 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 527 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
525 // make sure we got 2 or 3 arguments: the linda, a key and optionally a limit 528 // make sure we got 2 or 3 arguments: the linda, a slot and optionally a limit
526 int const _nargs{ lua_gettop(L_) }; 529 int const _nargs{ lua_gettop(L_) };
527 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); 530 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
528 // make sure we got a numeric limit, or "unlimited", (or nothing) 531 // make sure we got a numeric limit, or "unlimited", (or nothing)
@@ -531,7 +534,7 @@ LUAG_FUNC(linda_limit)
531 if (_val < 0) { 534 if (_val < 0) {
532 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0"); 535 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0");
533 } 536 }
534 // make sure the key is of a valid type 537 // make sure the slot is of a valid type
535 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); 538 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
536 539
537 KeeperCallResult _pushed; 540 KeeperCallResult _pushed;
@@ -539,8 +542,8 @@ LUAG_FUNC(linda_limit)
539 if (_unlimited) { 542 if (_unlimited) {
540 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited"); 543 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited");
541 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!) 544 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!)
542 lua_pop(L_, 1); // L_: linda key 545 lua_pop(L_, 1); // L_: linda slot
543 lua_pushinteger(L_, -1); // L_: linda key nil 546 lua_pushinteger(L_, -1); // L_: linda slot nil
544 } 547 }
545 Keeper* const _keeper{ _linda->whichKeeper() }; 548 Keeper* const _keeper{ _linda->whichKeeper() };
546 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 }); 549 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 });
@@ -572,12 +575,12 @@ LUAG_FUNC(linda_limit)
572 575
573/* 576/*
574 * 2 modes of operation 577 * 2 modes of operation
575 * [val, key]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) 578 * [val, slot]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] )
576 * Consumes a single value from the Linda, in any key. 579 * Consumes a single value from the Linda, in any slot.
577 * Returns: received value (which is consumed from the slot), and the key which had it 580 * Returns: received value (which is consumed from the slot), and the slot which had it
578 581
579 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) 582 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT])
580 * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. 583 * Consumes between min_COUNT and max_COUNT values from the linda, from a single slot.
581 * returns the actual consumed values, or nil if there weren't enough values to consume 584 * returns the actual consumed values, or nil if there weren't enough values to consume
582 */ 585 */
583LUAG_FUNC(linda_receive) 586LUAG_FUNC(linda_receive)
@@ -585,7 +588,7 @@ LUAG_FUNC(linda_receive)
585 static constexpr lua_CFunction _receive{ 588 static constexpr lua_CFunction _receive{
586 +[](lua_State* const L_) { 589 +[](lua_State* const L_) {
587 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 590 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
588 StackIndex _key_i{ 2 }; // index of first key, if timeout not there 591 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there
589 592
590 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 593 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
591 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 594 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
@@ -596,7 +599,7 @@ LUAG_FUNC(linda_receive)
596 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); 599 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0");
597 } 600 }
598 ++_key_i; 601 ++_key_i;
599 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key 602 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the slot
600 ++_key_i; 603 ++_key_i;
601 } 604 }
602 605
@@ -616,7 +619,7 @@ LUAG_FUNC(linda_receive)
616 raise_luaL_argerror(L_, StackIndex{ _key_i + 1 }, "bad min count"); 619 raise_luaL_argerror(L_, StackIndex{ _key_i + 1 }, "bad min count");
617 } 620 }
618 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min); 621 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min);
619 // don't forget to count the key in addition to the values 622 // don't forget to count the slot in addition to the values
620 ++_expected_pushed_min; 623 ++_expected_pushed_min;
621 ++_expected_pushed_max; 624 ++_expected_pushed_max;
622 if (_expected_pushed_min > _expected_pushed_max) { 625 if (_expected_pushed_min > _expected_pushed_max) {
@@ -627,7 +630,7 @@ LUAG_FUNC(linda_receive)
627 CheckKeyTypes(L_, _key_i, StackIndex{ lua_gettop(L_) }); 630 CheckKeyTypes(L_, _key_i, StackIndex{ lua_gettop(L_) });
628 // receive a single value, checking multiple slots 631 // receive a single value, checking multiple slots
629 _selected_keeper_receive = KEEPER_API(receive); 632 _selected_keeper_receive = KEEPER_API(receive);
630 // we expect a single (value, key) pair of returned values 633 // we expect a single (value, slot) pair of returned values
631 _expected_pushed_min = _expected_pushed_max = 2; 634 _expected_pushed_min = _expected_pushed_max = 2;
632 } 635 }
633 636
@@ -660,6 +663,9 @@ LUAG_FUNC(linda_receive)
660 } 663 }
661 if (_pushed.value() > 0) { 664 if (_pushed.value() > 0) {
662 LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max); 665 LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max);
666 if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) {
667 raise_luaL_error(L_, "Key is restricted");
668 }
663 _linda->readHappened.notify_all(); 669 _linda->readHappened.notify_all();
664 break; 670 break;
665 } 671 }
@@ -730,6 +736,49 @@ LUAG_FUNC(linda_receive)
730// ################################################################################################# 736// #################################################################################################
731 737
732/* 738/*
739 * "string" = linda:restrict(key_num|str|bool|lightuserdata, [string])
740 * "string" = linda:restrict(slot)
741 *
742 * Read or set restrict mode to 1 Linda slot.
743 */
744LUAG_FUNC(linda_restrict)
745{
746 static constexpr lua_CFunction _rstrct{
747 +[](lua_State* const L_) {
748 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
749 // make sure we got 2 or 3 arguments: the linda, a slot and optionally a restrict mode
750 int const _nargs{ lua_gettop(L_) };
751 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
752 // make sure we got a known restrict mode, (or nothing)
753 std::string_view const _mode{ luaG_tostring(L_, StackIndex{ 3 }) };
754 if (!_mode.empty() && (_mode != "none" && _mode != "set/get" && _mode != "send/receive")) {
755 raise_luaL_argerror(L_, StackIndex{ 3 }, "unknown restrict mode");
756 }
757 // make sure the slot is of a valid type
758 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
759
760 KeeperCallResult _pushed;
761 if (_linda->cancelStatus == Linda::Active) {
762 Keeper* const _keeper{ _linda->whichKeeper() };
763 _pushed = keeper_call(_keeper->K, KEEPER_API(restrict), L_, _linda, StackIndex{ 2 });
764 // we should get a single return value: the string describing the previous restrict mode
765 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, kIdxTop) == LuaType::STRING);
766 } else { // linda is cancelled
767 // do nothing and return nil,lanes.cancel_error
768 lua_pushnil(L_);
769 kCancelError.pushKey(L_);
770 _pushed.emplace(2);
771 }
772 // propagate returned values
773 return _pushed.value();
774 }
775 };
776 return Linda::ProtectedCall(L_, _rstrct);
777}
778
779// #################################################################################################
780
781/*
733 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) 782 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...)
734 * 783 *
735 * Send one or more values to a Linda. If there is a limit, all values must fit. 784 * Send one or more values to a Linda. If there is a limit, all values must fit.
@@ -743,7 +792,7 @@ LUAG_FUNC(linda_send)
743 static constexpr lua_CFunction _send{ 792 static constexpr lua_CFunction _send{
744 +[](lua_State* const L_) { 793 +[](lua_State* const L_) {
745 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 794 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
746 StackIndex _key_i{ 2 }; // index of first key, if timeout not there 795 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there
747 796
748 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 797 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
749 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 798 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
@@ -754,11 +803,11 @@ LUAG_FUNC(linda_send)
754 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); 803 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0");
755 } 804 }
756 ++_key_i; 805 ++_key_i;
757 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key 806 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the slot
758 ++_key_i; 807 ++_key_i;
759 } 808 }
760 809
761 // make sure the key is of a valid type 810 // make sure the slot is of a valid type
762 CheckKeyTypes(L_, _key_i, _key_i); 811 CheckKeyTypes(L_, _key_i, _key_i);
763 812
764 STACK_GROW(L_, 1); 813 STACK_GROW(L_, 1);
@@ -799,6 +848,9 @@ LUAG_FUNC(linda_send)
799 } 848 }
800 LUA_ASSERT(L_, _pushed.value() == 1); 849 LUA_ASSERT(L_, _pushed.value() == 1);
801 850
851 if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) {
852 raise_luaL_error(L_, "Key is restricted");
853 }
802 _ret = lua_toboolean(L_, -1) ? true : false; 854 _ret = lua_toboolean(L_, -1) ? true : false;
803 lua_pop(L_, 1); 855 lua_pop(L_, 1);
804 856
@@ -884,7 +936,7 @@ LUAG_FUNC(linda_set)
884 +[](lua_State* const L_) { 936 +[](lua_State* const L_) {
885 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 937 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
886 bool const _has_data{ lua_gettop(L_) > 2 }; 938 bool const _has_data{ lua_gettop(L_) > 2 };
887 // make sure the key is of a valid type (throws an error if not the case) 939 // make sure the slot is of a valid type (throws an error if not the case)
888 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); 940 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
889 941
890 KeeperCallResult _pushed; 942 KeeperCallResult _pushed;
@@ -892,6 +944,9 @@ LUAG_FUNC(linda_set)
892 Keeper* const _keeper{ _linda->whichKeeper() }; 944 Keeper* const _keeper{ _linda->whichKeeper() };
893 _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, StackIndex{ 2 }); 945 _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, StackIndex{ 2 });
894 if (_pushed.has_value()) { // no error? 946 if (_pushed.has_value()) { // no error?
947 if (kRestrictedChannel.equals(L_, kIdxTop)) {
948 raise_luaL_error(L_, "Key is restricted");
949 }
895 LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, kIdxTop) == LuaType::STRING && luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); 950 LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, kIdxTop) == LuaType::STRING && luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN);
896 951
897 if (_has_data) { 952 if (_has_data) {
@@ -899,7 +954,7 @@ LUAG_FUNC(linda_set)
899 _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area 954 _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area
900 } 955 }
901 if (lua_toboolean(L_, -2)) { 956 if (lua_toboolean(L_, -2)) {
902 // the key was full, but it is no longer the case, tell writers they should wake 957 // the slot was full, but it is no longer the case, tell writers they should wake
903 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area 958 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
904 } 959 }
905 } 960 }
@@ -992,6 +1047,7 @@ namespace {
992 { "get", LG_linda_get }, 1047 { "get", LG_linda_get },
993 { "limit", LG_linda_limit }, 1048 { "limit", LG_linda_limit },
994 { "receive", LG_linda_receive }, 1049 { "receive", LG_linda_receive },
1050 { "restrict", LG_linda_restrict },
995 { "send", LG_linda_send }, 1051 { "send", LG_linda_send },
996 { "set", LG_linda_set }, 1052 { "set", LG_linda_set },
997 { "wake", LG_linda_wake }, 1053 { "wake", LG_linda_wake },