aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-06-26 17:45:16 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-06-26 17:45:16 +0200
commit62a7eab66f8f6af66c94390138815c3171b62810 (patch)
tree0b93a11f97e186d82a931b40ae092f7438ca3f31 /src
parente715be93bf00a452a6c2f1cd86a60b4ef5f30b07 (diff)
downloadlanes-62a7eab66f8f6af66c94390138815c3171b62810.tar.gz
lanes-62a7eab66f8f6af66c94390138815c3171b62810.tar.bz2
lanes-62a7eab66f8f6af66c94390138815c3171b62810.zip
Change linda:limit()
* read the current limit of a key if no limit is provided * "unlimited" is to be used to clear the limit * fix linda:set() not ignoring the limit
Diffstat (limited to '')
-rw-r--r--src/keeper.cpp53
-rw-r--r--src/keeper.h1
-rw-r--r--src/linda.cpp29
-rw-r--r--src/macros_and_utils.h2
4 files changed, 56 insertions, 29 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 3054f86..ae09b37 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -68,7 +68,7 @@ class KeyUD
68 public: 68 public:
69 int first{ 1 }; 69 int first{ 1 };
70 int count{ 0 }; 70 int count{ 0 };
71 int limit{ -1 }; 71 LindaLimit limit{ -1 };
72 72
73 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents 73 // a fifo full userdata has one uservalue, the table that holds the actual fifo contents
74 [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return luaG_newuserdatauv<KeyUD>(L_, 1); } 74 [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return luaG_newuserdatauv<KeyUD>(L_, 1); }
@@ -76,19 +76,19 @@ class KeyUD
76 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 76 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
77 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); } 77 static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); }
78 78
79 [[nodiscard]] bool changeLimit(int limit_); 79 [[nodiscard]] bool changeLimit(LindaLimit limit_);
80 [[nodiscard]] static KeyUD* Create(KeeperState K_); 80 [[nodiscard]] static KeyUD* Create(KeeperState K_);
81 [[nodiscard]] static KeyUD* GetPtr(KeeperState K_, int idx_); 81 [[nodiscard]] static KeyUD* GetPtr(KeeperState K_, int idx_);
82 void peek(KeeperState K_, int count_) const; // keepercall_get 82 void peek(KeeperState K_, int count_) const; // keepercall_get
83 [[nodiscard]] int pop(KeeperState K_, int minCount_, int maxCount_); // keepercall_receive[_batched] 83 [[nodiscard]] int pop(KeeperState K_, int minCount_, int maxCount_); // keepercall_receive[_batched]
84 void prepareAccess(KeeperState K_, int idx_) const; 84 void prepareAccess(KeeperState K_, int idx_) const;
85 [[nodiscard]] bool push(KeeperState K_, int count_); // keepercall_send 85 [[nodiscard]] bool push(KeeperState K_, int count_, bool enforceLimit_); // keepercall_send and keepercall_set
86 [[nodiscard]] bool reset(KeeperState K_); 86 [[nodiscard]] bool reset(KeeperState K_);
87}; 87};
88 88
89// ################################################################################################# 89// #################################################################################################
90 90
91bool KeyUD::changeLimit(int const limit_) 91bool KeyUD::changeLimit(LindaLimit const limit_)
92{ 92{
93 bool const _newSlackAvailable{ 93 bool const _newSlackAvailable{
94 ((limit >= 0) && (count >= limit)) // then: the key was full if limited and count exceeded the previous limit 94 ((limit >= 0) && (count >= limit)) // then: the key was full if limited and count exceeded the previous limit
@@ -209,11 +209,11 @@ void KeyUD::prepareAccess(KeeperState const K_, int const idx_) const
209 209
210// in: expect this val... on top of the stack 210// in: expect this val... on top of the stack
211// out: nothing, removes all pushed values from the stack 211// out: nothing, removes all pushed values from the stack
212bool KeyUD::push(KeeperState const K_, int const count_) 212bool KeyUD::push(KeeperState const K_, int const count_, bool const enforceLimit_)
213{ 213{
214 int const _fifoIdx{ luaG_absindex(K_, -1 - count_) }; 214 int const _fifoIdx{ luaG_absindex(K_, -1 - count_) };
215 LUA_ASSERT(K_, KeyUD::GetPtr(K_, _fifoIdx) == this); // K_: this val... 215 LUA_ASSERT(K_, KeyUD::GetPtr(K_, _fifoIdx) == this); // K_: this val...
216 if (limit >= 0 && count + count_ > limit) { // not enough room 216 if (enforceLimit_ && (limit >= 0) && (count + count_ > limit)) { // not enough room
217 return false; 217 return false;
218 } 218 }
219 219
@@ -239,7 +239,8 @@ bool KeyUD::reset(KeeperState const K_)
239 STACK_CHECK_START_REL(K_, 0); 239 STACK_CHECK_START_REL(K_, 0);
240 bool const _wasFull{ (limit > 0) && (count >= limit) }; 240 bool const _wasFull{ (limit > 0) && (count >= limit) };
241 // empty the KeyUD: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 241 // empty the KeyUD: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
242 lua_newtable(K_); // K_: KeysDB key val... KeyUD {} 242 // if we have an actual limit, use it to preconfigure the table
243 lua_createtable(K_, (limit <= 0) ? 0 : limit, 0); // K_: KeysDB key val... KeyUD {}
243 lua_setiuservalue(K_, -2, kContentsTableIndex); // K_: KeysDB key val... KeyUD 244 lua_setiuservalue(K_, -2, kContentsTableIndex); // K_: KeysDB key val... KeyUD
244 first = 1; 245 first = 1;
245 count = 0; 246 count = 0;
@@ -397,23 +398,34 @@ int keepercall_get(lua_State* const L_)
397int keepercall_limit(lua_State* const L_) 398int keepercall_limit(lua_State* const L_)
398{ 399{
399 KeeperState const _K{ L_ }; 400 KeeperState const _K{ L_ };
400 int const _limit{ static_cast<int>(luaL_optinteger(_K, 3, -1)) }; // -1 if we read nil because the argument is absent 401 // no limit to set, means we read and return the current limit instead
402 bool const _reading{ lua_gettop(_K) == 2 };
403 LindaLimit const _limit{ static_cast<LindaLimit::type>(luaL_optinteger(_K, 3, -1)) }; // -1 if we read nil because the argument is absent
401 lua_settop(_K, 2); // _K: linda key 404 lua_settop(_K, 2); // _K: linda key
402 PushKeysDB(_K, 1); // _K: linda key KeysDB 405 PushKeysDB(_K, 1); // _K: linda key KeysDB
403 lua_replace(_K, 1); // _K: KeysDB key 406 lua_replace(_K, 1); // _K: KeysDB key
404 lua_pushvalue(_K, -1); // _K: KeysDB key key 407 lua_pushvalue(_K, -1); // _K: KeysDB key key
405 lua_rawget(_K, -3); // _K: KeysDB key KeyUD|nil 408 lua_rawget(_K, -3); // _K: KeysDB key KeyUD|nil
406 KeyUD* _key{ KeyUD::GetPtr(_K, -1) }; 409 KeyUD* _key{ KeyUD::GetPtr(_K, -1) };
407 if (_key == nullptr) { // _K: KeysDB key nil 410 if (_reading) {
408 lua_pop(_K, 1); // _K: KeysDB key 411 if (_key && _key->limit >= 0) {
409 _key = KeyUD::Create(_K); // _K: KeysDB key KeyUD 412 lua_pushinteger(_K, _key->limit); // _K: KeysDB key KeyUD limit
410 lua_rawset(_K, -3); // _K: KeysDB 413 } else { // if the key doesn't exist, it is unlimited by default
414 luaG_pushstring(_K, "unlimited"); // _K: KeysDB key KeyUD "unlimited"
415 }
416 // return a single value: the limit of the key
417 } else {
418 if (_key == nullptr) { // _K: KeysDB key nil
419 lua_pop(_K, 1); // _K: KeysDB key
420 _key = KeyUD::Create(_K); // _K: KeysDB key KeyUD
421 lua_rawset(_K, -3); // _K: KeysDB
422 }
423 // remove any clutter on the stack
424 lua_settop(_K, 0); // _K:
425 // return true if we decide that blocked threads waiting to write on that key should be awakened
426 // this is the case if we detect the key was full but it is no longer the case
427 lua_pushboolean(_K, _key->changeLimit(_limit) ? 1 : 0); // _K: bool
411 } 428 }
412 // remove any clutter on the stack
413 lua_settop(_K, 0); // _K:
414 // return true if we decide that blocked threads waiting to write on that key should be awakened
415 // this is the case if we detect the key was full but it is no longer the case
416 lua_pushboolean(_K, _key->changeLimit(_limit) ? 1 : 0); // _K: bool
417 return 1; 429 return 1;
418} 430}
419 431
@@ -502,7 +514,7 @@ int keepercall_send(lua_State* const L_)
502 lua_pop(_K, 1); // _K: linda KeyUD val... 514 lua_pop(_K, 1); // _K: linda KeyUD val...
503 STACK_CHECK(_K, 0); 515 STACK_CHECK(_K, 0);
504 KeyUD* const _key{ KeyUD::GetPtr(_K, 2) }; 516 KeyUD* const _key{ KeyUD::GetPtr(_K, 2) };
505 if (_key && _key->push(_K, _n)) { // not enough room? 517 if (_key && _key->push(_K, _n, true)) { // not enough room?
506 lua_settop(_K, 0); // _K: 518 lua_settop(_K, 0); // _K:
507 lua_pushboolean(_K, 1); // _K: true 519 lua_pushboolean(_K, 1); // _K: true
508 } else { 520 } else {
@@ -527,8 +539,7 @@ int keepercall_set(lua_State* const L_)
527 PushKeysDB(_K, 1); // _K: linda key val... KeysDB 539 PushKeysDB(_K, 1); // _K: linda key val... KeysDB
528 lua_replace(_K, 1); // _K: KeysDB key val... 540 lua_replace(_K, 1); // _K: KeysDB key val...
529 541
530 // make sure we have a value on the stack 542 if (lua_gettop(_K) == 2) { // no value to set // _K: KeysDB key
531 if (lua_gettop(_K) == 2) { // _K: KeysDB key
532 lua_pushvalue(_K, -1); // _K: KeysDB key key 543 lua_pushvalue(_K, -1); // _K: KeysDB key key
533 lua_rawget(_K, 1); // _K: KeysDB key KeyUD|nil 544 lua_rawget(_K, 1); // _K: KeysDB key KeyUD|nil
534 // empty the KeyUD for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! 545 // empty the KeyUD for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged!
@@ -563,7 +574,7 @@ int keepercall_set(lua_State* const L_)
563 } 574 }
564 // replace the key with the KeyUD in the stack 575 // replace the key with the KeyUD in the stack
565 lua_replace(_K, -2 - _count); // _K: KeysDB KeyUD val... 576 lua_replace(_K, -2 - _count); // _K: KeysDB KeyUD val...
566 [[maybe_unused]] bool const _pushed{ _key->push(_K, _count) }; // _K: KeysDB 577 [[maybe_unused]] bool const _pushed{ _key->push(_K, _count, false) }; // _K: KeysDB
567 } 578 }
568 // stack isn't the same here depending on what we did before, but that's not a problem 579 // stack isn't the same here depending on what we did before, but that's not a problem
569 lua_pushboolean(_K, _should_wake_writers ? 1 : 0); // _K: ... bool 580 lua_pushboolean(_K, _should_wake_writers ? 1 : 0); // _K: ... bool
diff --git a/src/keeper.h b/src/keeper.h
index 102d006..05e3547 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -8,6 +8,7 @@ enum class LookupMode;
8class Universe; 8class Universe;
9 9
10using KeeperState = Unique<lua_State*>; 10using KeeperState = Unique<lua_State*>;
11using LindaLimit = Unique<int>;
11 12
12// ################################################################################################# 13// #################################################################################################
13 14
diff --git a/src/linda.cpp b/src/linda.cpp
index 079ab9d..f4dd7e7 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -452,21 +452,23 @@ LUAG_FUNC(linda_get)
452 452
453/* 453/*
454 * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int]) 454 * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int])
455 * "unlimited"|number = linda:limit(key)
455 * 456 *
456 * Set limit to 1 Linda keys. 457 * Read or set limit to 1 Linda keys.
457 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so 458 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so
458 * Limit can be 0 to completely block everything, nil to reset 459 * Limit can be 0 to completely block everything, "unlimited" to reset
459 */ 460 */
460LUAG_FUNC(linda_limit) 461LUAG_FUNC(linda_limit)
461{ 462{
462 static constexpr lua_CFunction _limit{ 463 static constexpr lua_CFunction _limit{
463 +[](lua_State* const L_) { 464 +[](lua_State* const L_) {
464 Linda* const _linda{ ToLinda<false>(L_, 1) }; 465 Linda* const _linda{ ToLinda<false>(L_, 1) };
465 // make sure we got 3 arguments: the linda, a key and a limit 466 // make sure we got 2 or 3 arguments: the linda, a key and optionally a limit
466 int const _nargs{ lua_gettop(L_) }; 467 int const _nargs{ lua_gettop(L_) };
467 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); 468 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
468 // make sure we got a numeric limit 469 // make sure we got a numeric limit, or "unlimited", (or nothing)
469 lua_Integer const _val{ luaL_optinteger(L_, 3, 0) }; 470 bool const _unlimited{ luaG_tostring(L_, 3) == "unlimited" };
471 LindaLimit const _val{ _unlimited ? std::numeric_limits<LindaLimit::type>::max() : LindaLimit{ static_cast<LindaLimit::type>(luaL_optinteger(L_, 3, 0)) } };
470 if (_val < 0) { 472 if (_val < 0) {
471 raise_luaL_argerror(L_, 3, "limit must be >= 0"); 473 raise_luaL_argerror(L_, 3, "limit must be >= 0");
472 } 474 }
@@ -476,10 +478,21 @@ LUAG_FUNC(linda_limit)
476 KeeperCallResult _pushed; 478 KeeperCallResult _pushed;
477 if (_linda->cancelRequest == CancelRequest::None) { 479 if (_linda->cancelRequest == CancelRequest::None) {
478 Keeper* const _keeper{ _linda->whichKeeper() }; 480 Keeper* const _keeper{ _linda->whichKeeper() };
481 if (_unlimited) {
482 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, 3) == "unlimited");
483 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!)
484 lua_pop(L_, 1); // L_: linda key
485 lua_pushinteger(L_, -1); // L_: linda key nil
486 }
479 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2); 487 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2);
480 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 488 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1));
481 if (lua_toboolean(L_, -1)) { 489 if (_nargs == 3) { // 3 args: setting the limit
482 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area 490 LUA_ASSERT(L_, luaG_type(L_, -1) == LuaType::BOOLEAN); // changing the limit: no error, boolean value saying if we should wake blocked writer threads
491 if (lua_toboolean(L_, -1)) {
492 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
493 }
494 } else { // 2 args: reading the limit
495 LUA_ASSERT(L_, luaG_type(L_, -1) == LuaType::NUMBER || luaG_tostring(L_, -1) == "unlimited"); // reading the limit: a number >=0 or "unlimited"
483 } 496 }
484 } else { // linda is cancelled 497 } else { // linda is cancelled
485 // do nothing and return nil,lanes.cancel_error 498 // do nothing and return nil,lanes.cancel_error
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index 787cf03..1b1ced6 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -32,6 +32,7 @@ class Unique
32 T val; 32 T val;
33 33
34 public: 34 public:
35 using type = T;
35 Unique() = default; 36 Unique() = default;
36 operator T() const { return val; } 37 operator T() const { return val; }
37 explicit Unique(T b_) 38 explicit Unique(T b_)
@@ -45,6 +46,7 @@ class Unique<T, lambda, std::enable_if_t<!std::is_scalar_v<T>>>
45: public T 46: public T
46{ 47{
47 public: 48 public:
49 using type = T;
48 using T::T; 50 using T::T;
49 explicit Unique(T const& b_) 51 explicit Unique(T const& b_)
50 : T{ b_ } 52 : T{ b_ }