diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-26 17:45:16 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-26 17:45:16 +0200 |
commit | 62a7eab66f8f6af66c94390138815c3171b62810 (patch) | |
tree | 0b93a11f97e186d82a931b40ae092f7438ca3f31 /src | |
parent | e715be93bf00a452a6c2f1cd86a60b4ef5f30b07 (diff) | |
download | lanes-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.cpp | 53 | ||||
-rw-r--r-- | src/keeper.h | 1 | ||||
-rw-r--r-- | src/linda.cpp | 29 | ||||
-rw-r--r-- | src/macros_and_utils.h | 2 |
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 | ||
91 | bool KeyUD::changeLimit(int const limit_) | 91 | bool 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 |
212 | bool KeyUD::push(KeeperState const K_, int const count_) | 212 | bool 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_) | |||
397 | int keepercall_limit(lua_State* const L_) | 398 | int 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; | |||
8 | class Universe; | 8 | class Universe; |
9 | 9 | ||
10 | using KeeperState = Unique<lua_State*>; | 10 | using KeeperState = Unique<lua_State*>; |
11 | using 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 | */ |
460 | LUAG_FUNC(linda_limit) | 461 | LUAG_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_ } |