diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/keeper.cpp | 75 | ||||
| -rw-r--r-- | src/lane.cpp | 2 | ||||
| -rw-r--r-- | src/lanes.lua | 3 | ||||
| -rw-r--r-- | src/linda.cpp | 20 |
4 files changed, 72 insertions, 28 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp index ae09b37..cfeebbb 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
| @@ -66,6 +66,10 @@ class KeyUD | |||
| 66 | static constexpr int kContentsTableIndex{ 1 }; | 66 | static constexpr int kContentsTableIndex{ 1 }; |
| 67 | 67 | ||
| 68 | public: | 68 | public: |
| 69 | static constexpr std::string_view kUnder{ "under" }; | ||
| 70 | static constexpr std::string_view kExact{ "exact" }; | ||
| 71 | static constexpr std::string_view kOver{ "over" }; | ||
| 72 | |||
| 69 | int first{ 1 }; | 73 | int first{ 1 }; |
| 70 | int count{ 0 }; | 74 | int count{ 0 }; |
| 71 | LindaLimit limit{ -1 }; | 75 | LindaLimit limit{ -1 }; |
| @@ -83,6 +87,8 @@ class KeyUD | |||
| 83 | [[nodiscard]] int pop(KeeperState K_, int minCount_, int maxCount_); // keepercall_receive[_batched] | 87 | [[nodiscard]] int pop(KeeperState K_, int minCount_, int maxCount_); // keepercall_receive[_batched] |
| 84 | void prepareAccess(KeeperState K_, int idx_) const; | 88 | void prepareAccess(KeeperState K_, int idx_) const; |
| 85 | [[nodiscard]] bool push(KeeperState K_, int count_, bool enforceLimit_); // keepercall_send and keepercall_set | 89 | [[nodiscard]] bool push(KeeperState K_, int count_, bool enforceLimit_); // keepercall_send and keepercall_set |
| 90 | void pushFillStatus(KeeperState K_) const; | ||
| 91 | static void PushFillStatus(KeeperState K_, KeyUD const* key_); | ||
| 86 | [[nodiscard]] bool reset(KeeperState K_); | 92 | [[nodiscard]] bool reset(KeeperState K_); |
| 87 | }; | 93 | }; |
| 88 | 94 | ||
| @@ -232,6 +238,35 @@ bool KeyUD::push(KeeperState const K_, int const count_, bool const enforceLimit | |||
| 232 | 238 | ||
| 233 | // ################################################################################################# | 239 | // ################################################################################################# |
| 234 | 240 | ||
| 241 | void KeyUD::pushFillStatus(KeeperState const K_) const | ||
| 242 | { | ||
| 243 | if (limit < 0) { | ||
| 244 | luaG_pushstring(K_, kUnder); | ||
| 245 | return; | ||
| 246 | } | ||
| 247 | int const _delta{limit - count}; | ||
| 248 | if (_delta < 0) { | ||
| 249 | luaG_pushstring(K_, kOver); | ||
| 250 | } else if (_delta > 0) { | ||
| 251 | luaG_pushstring(K_, kUnder); | ||
| 252 | } else { | ||
| 253 | luaG_pushstring(K_, kExact); | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | // ################################################################################################# | ||
| 258 | |||
| 259 | void KeyUD::PushFillStatus(KeeperState const K_, KeyUD const* const key_) | ||
| 260 | { | ||
| 261 | if (key_) { | ||
| 262 | key_->pushFillStatus(K_); // _K: ... <fill status> | ||
| 263 | } else { | ||
| 264 | luaG_pushstring(K_, KeyUD::kUnder); // _K: ... "under" | ||
| 265 | } | ||
| 266 | } | ||
| 267 | |||
| 268 | // ################################################################################################# | ||
| 269 | |||
| 235 | // expects 'this' on top of the stack | 270 | // expects 'this' on top of the stack |
| 236 | bool KeyUD::reset(KeeperState const K_) | 271 | bool KeyUD::reset(KeeperState const K_) |
| 237 | { | 272 | { |
| @@ -394,10 +429,11 @@ int keepercall_get(lua_State* const L_) | |||
| 394 | // ################################################################################################# | 429 | // ################################################################################################# |
| 395 | 430 | ||
| 396 | // in: linda key [n|nil] | 431 | // in: linda key [n|nil] |
| 397 | // out: true or nil | 432 | // out: boolean, <fill status: string> |
| 398 | int keepercall_limit(lua_State* const L_) | 433 | int keepercall_limit(lua_State* const L_) |
| 399 | { | 434 | { |
| 400 | KeeperState const _K{ L_ }; | 435 | KeeperState const _K{ L_ }; |
| 436 | STACK_CHECK_START_ABS(_K, lua_gettop(_K)); | ||
| 401 | // no limit to set, means we read and return the current limit instead | 437 | // no limit to set, means we read and return the current limit instead |
| 402 | bool const _reading{ lua_gettop(_K) == 2 }; | 438 | 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 | 439 | LindaLimit const _limit{ static_cast<LindaLimit::type>(luaL_optinteger(_K, 3, -1)) }; // -1 if we read nil because the argument is absent |
| @@ -408,10 +444,12 @@ int keepercall_limit(lua_State* const L_) | |||
| 408 | lua_rawget(_K, -3); // _K: KeysDB key KeyUD|nil | 444 | lua_rawget(_K, -3); // _K: KeysDB key KeyUD|nil |
| 409 | KeyUD* _key{ KeyUD::GetPtr(_K, -1) }; | 445 | KeyUD* _key{ KeyUD::GetPtr(_K, -1) }; |
| 410 | if (_reading) { | 446 | if (_reading) { |
| 447 | // remove any clutter on the stack | ||
| 448 | lua_settop(_K, 0); // _K: | ||
| 411 | if (_key && _key->limit >= 0) { | 449 | if (_key && _key->limit >= 0) { |
| 412 | lua_pushinteger(_K, _key->limit); // _K: KeysDB key KeyUD limit | 450 | lua_pushinteger(_K, _key->limit); // _K: limit |
| 413 | } else { // if the key doesn't exist, it is unlimited by default | 451 | } else { // if the key doesn't exist, it is unlimited by default |
| 414 | luaG_pushstring(_K, "unlimited"); // _K: KeysDB key KeyUD "unlimited" | 452 | luaG_pushstring(_K, "unlimited"); // _K: "unlimited" |
| 415 | } | 453 | } |
| 416 | // return a single value: the limit of the key | 454 | // return a single value: the limit of the key |
| 417 | } else { | 455 | } else { |
| @@ -426,7 +464,9 @@ int keepercall_limit(lua_State* const L_) | |||
| 426 | // this is the case if we detect the key was full but it is no longer the case | 464 | // 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 | 465 | lua_pushboolean(_K, _key->changeLimit(_limit) ? 1 : 0); // _K: bool |
| 428 | } | 466 | } |
| 429 | return 1; | 467 | KeyUD::PushFillStatus(_K, _key); // _K: limit|bool <fill status> |
| 468 | STACK_CHECK(_K, 2); | ||
| 469 | return 2; | ||
| 430 | } | 470 | } |
| 431 | 471 | ||
| 432 | // ################################################################################################# | 472 | // ################################################################################################# |
| @@ -538,12 +578,12 @@ int keepercall_set(lua_State* const L_) | |||
| 538 | // retrieve KeysDB associated with the linda | 578 | // retrieve KeysDB associated with the linda |
| 539 | PushKeysDB(_K, 1); // _K: linda key val... KeysDB | 579 | PushKeysDB(_K, 1); // _K: linda key val... KeysDB |
| 540 | lua_replace(_K, 1); // _K: KeysDB key val... | 580 | lua_replace(_K, 1); // _K: KeysDB key val... |
| 581 | lua_pushvalue(_K, 2); // _K: KeysDB key val... key | ||
| 582 | lua_rawget(_K, 1); // _K: KeysDB key val KeyUD|nil | ||
| 583 | KeyUD* _key{ KeyUD::GetPtr(_K, -1) }; | ||
| 541 | 584 | ||
| 542 | if (lua_gettop(_K) == 2) { // no value to set // _K: KeysDB key | 585 | if (lua_gettop(_K) == 3) { // no value to set // _K: KeysDB key KeyUD|nil |
| 543 | lua_pushvalue(_K, -1); // _K: KeysDB key key | ||
| 544 | lua_rawget(_K, 1); // _K: KeysDB key KeyUD|nil | ||
| 545 | // empty the KeyUD for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 586 | // empty the KeyUD for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
| 546 | KeyUD* const _key{ KeyUD::GetPtr(_K, -1) }; | ||
| 547 | if (_key != nullptr) { // might be nullptr if we set a nonexistent key to nil // _K: KeysDB key KeyUD | 587 | if (_key != nullptr) { // might be nullptr if we set a nonexistent key to nil // _K: KeysDB key KeyUD |
| 548 | if (_key->limit < 0) { // KeyUD limit value is the default (unlimited): we can totally remove it | 588 | if (_key->limit < 0) { // KeyUD limit value is the default (unlimited): we can totally remove it |
| 549 | lua_pop(_K, 1); // _K: KeysDB key | 589 | lua_pop(_K, 1); // _K: KeysDB key |
| @@ -555,18 +595,17 @@ int keepercall_set(lua_State* const L_) | |||
| 555 | _should_wake_writers = _key->reset(_K); | 595 | _should_wake_writers = _key->reset(_K); |
| 556 | } | 596 | } |
| 557 | } | 597 | } |
| 598 | lua_settop(_K, 0); // we are done, remove everything // _K: | ||
| 558 | } else { // set/replace contents stored at the specified key? | 599 | } else { // set/replace contents stored at the specified key? |
| 559 | int const _count{ lua_gettop(_K) - 2 }; // number of items we want to store | 600 | int const _count{ lua_gettop(_K) - 3 }; // number of items we want to store // _K: KeysDB key val... KeyUD|nil |
| 560 | lua_pushvalue(_K, 2); // _K: KeysDB key val... key | 601 | if (_key == nullptr) { // can be nullptr if we store a value at a new key // _K: KeysDB key val... nil |
| 561 | lua_rawget(_K, 1); // _K: KeysDB key val... KeyUD|nil | 602 | assert(lua_isnil(_K, -1)); |
| 562 | KeyUD* _key{ KeyUD::GetPtr(_K, -1) }; | ||
| 563 | if (_key == nullptr) { // can be nullptr if we store a value at a new key // KeysDB key val... nil | ||
| 564 | // no need to wake writers in that case, because a writer can't wait on an inexistent key | ||
| 565 | lua_pop(_K, 1); // _K: KeysDB key val... | 603 | lua_pop(_K, 1); // _K: KeysDB key val... |
| 566 | _key = KeyUD::Create(KeeperState{ _K }); // _K: KeysDB key val... KeyUD | 604 | _key = KeyUD::Create(KeeperState{ _K }); // _K: KeysDB key val... KeyUD |
| 567 | lua_pushvalue(_K, 2); // _K: KeysDB key val... KeyUD key | 605 | lua_pushvalue(_K, 2); // _K: KeysDB key val... KeyUD key |
| 568 | lua_pushvalue(_K, -2); // _K: KeysDB key val... KeyUD key KeyUD | 606 | lua_pushvalue(_K, -2); // _K: KeysDB key val... KeyUD key KeyUD |
| 569 | lua_rawset(_K, 1); // _K: KeysDB key val... KeyUD | 607 | lua_rawset(_K, 1); // _K: KeysDB key val... KeyUD |
| 608 | // no need to wake writers, because a writer can't wait on an inexistent key | ||
| 570 | } else { // _K: KeysDB key val... KeyUD | 609 | } else { // _K: KeysDB key val... KeyUD |
| 571 | // the KeyUD exists, we just want to update its contents | 610 | // the KeyUD exists, we just want to update its contents |
| 572 | // we create room if the KeyUD was full but we didn't refill it to the brim with new data | 611 | // we create room if the KeyUD was full but we didn't refill it to the brim with new data |
| @@ -575,10 +614,12 @@ int keepercall_set(lua_State* const L_) | |||
| 575 | // replace the key with the KeyUD in the stack | 614 | // replace the key with the KeyUD in the stack |
| 576 | lua_replace(_K, -2 - _count); // _K: KeysDB KeyUD val... | 615 | lua_replace(_K, -2 - _count); // _K: KeysDB KeyUD val... |
| 577 | [[maybe_unused]] bool const _pushed{ _key->push(_K, _count, false) }; // _K: KeysDB | 616 | [[maybe_unused]] bool const _pushed{ _key->push(_K, _count, false) }; // _K: KeysDB |
| 617 | lua_pop(_K, 1); // _K: | ||
| 578 | } | 618 | } |
| 579 | // stack isn't the same here depending on what we did before, but that's not a problem | 619 | assert(lua_gettop(_K) == 0); |
| 580 | lua_pushboolean(_K, _should_wake_writers ? 1 : 0); // _K: ... bool | 620 | lua_pushboolean(_K, _should_wake_writers ? 1 : 0); // _K: bool |
| 581 | return 1; | 621 | KeyUD::PushFillStatus(_K, _key); // _K: bool <fill status> |
| 622 | return 2; | ||
| 582 | } | 623 | } |
| 583 | 624 | ||
| 584 | // ################################################################################################# | 625 | // ################################################################################################# |
diff --git a/src/lane.cpp b/src/lane.cpp index d4175c6..ba24af3 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
| @@ -435,7 +435,7 @@ static constexpr RegistryUniqueKey kStackTraceRegKey{ 0x3F327747CACAA904ull }; | |||
| 435 | [[nodiscard]] static int lane_error(lua_State* L_) | 435 | [[nodiscard]] static int lane_error(lua_State* L_) |
| 436 | { | 436 | { |
| 437 | // error message (any type) | 437 | // error message (any type) |
| 438 | STACK_CHECK_START_ABS(L_, 1); // L_: some_error | 438 | STACK_CHECK_START_ABS(L_, 1); // L_: some_error |
| 439 | 439 | ||
| 440 | // Don't do stack survey for cancelled lanes. | 440 | // Don't do stack survey for cancelled lanes. |
| 441 | // | 441 | // |
diff --git a/src/lanes.lua b/src/lanes.lua index 6a4f149..48ebeb6 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
| @@ -792,7 +792,8 @@ local genatomic = function(linda_, key_, initial_val_) | |||
| 792 | if val ~= cancel_error then | 792 | if val ~= cancel_error then |
| 793 | val = val + (diff_ or 1.0) | 793 | val = val + (diff_ or 1.0) |
| 794 | -- set() releases the lock by emptying queue | 794 | -- set() releases the lock by emptying queue |
| 795 | if linda_:set(key_, val) == cancel_error then | 795 | local _res, _err = linda_:set(key_, val) |
| 796 | if _err == cancel_error then | ||
| 796 | val = cancel_error | 797 | val = cancel_error |
| 797 | end | 798 | end |
| 798 | end | 799 | end |
diff --git a/src/linda.cpp b/src/linda.cpp index f4dd7e7..13627aa 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
| @@ -477,22 +477,24 @@ LUAG_FUNC(linda_limit) | |||
| 477 | 477 | ||
| 478 | KeeperCallResult _pushed; | 478 | KeeperCallResult _pushed; |
| 479 | if (_linda->cancelRequest == CancelRequest::None) { | 479 | if (_linda->cancelRequest == CancelRequest::None) { |
| 480 | Keeper* const _keeper{ _linda->whichKeeper() }; | ||
| 481 | if (_unlimited) { | 480 | if (_unlimited) { |
| 482 | LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, 3) == "unlimited"); | 481 | 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!) | 482 | // 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 | 483 | lua_pop(L_, 1); // L_: linda key |
| 485 | lua_pushinteger(L_, -1); // L_: linda key nil | 484 | lua_pushinteger(L_, -1); // L_: linda key nil |
| 486 | } | 485 | } |
| 486 | Keeper* const _keeper{ _linda->whichKeeper() }; | ||
| 487 | _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2); | 487 | _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2); |
| 488 | LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1)); | 488 | LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 2) && luaG_type(L_, -1) == LuaType::STRING); |
| 489 | if (_nargs == 3) { // 3 args: setting the limit | 489 | if (_nargs == 3) { // 3 args: setting the limit |
| 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 | 490 | // changing the limit: no error, boolean value saying if we should wake blocked writer threads |
| 491 | if (lua_toboolean(L_, -1)) { | 491 | LUA_ASSERT(L_, luaG_type(L_, -2) == LuaType::BOOLEAN); // L_: bool string |
| 492 | if (lua_toboolean(L_, -2)) { | ||
| 492 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | 493 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area |
| 493 | } | 494 | } |
| 494 | } else { // 2 args: reading the limit | 495 | } 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" | 496 | // reading the limit: a number >=0 or "unlimited" |
| 497 | LUA_ASSERT(L_, luaG_type(L_, -2) == LuaType::NUMBER || luaG_tostring(L_, -2) == "unlimited"); | ||
| 496 | } | 498 | } |
| 497 | } else { // linda is cancelled | 499 | } else { // linda is cancelled |
| 498 | // do nothing and return nil,lanes.cancel_error | 500 | // do nothing and return nil,lanes.cancel_error |
| @@ -500,7 +502,7 @@ LUAG_FUNC(linda_limit) | |||
| 500 | kCancelError.pushKey(L_); | 502 | kCancelError.pushKey(L_); |
| 501 | _pushed.emplace(2); | 503 | _pushed.emplace(2); |
| 502 | } | 504 | } |
| 503 | // propagate pushed boolean if any | 505 | // propagate returned values |
| 504 | return _pushed.value(); | 506 | return _pushed.value(); |
| 505 | } | 507 | } |
| 506 | }; | 508 | }; |
| @@ -807,7 +809,7 @@ LUAG_FUNC(linda_send) | |||
| 807 | // ################################################################################################# | 809 | // ################################################################################################# |
| 808 | 810 | ||
| 809 | /* | 811 | /* |
| 810 | * [true|nil,lanes.cancel_error] = linda:set(key_num|str|bool|lightuserdata [, value [, ...]]) | 812 | * (boolean,string)|(nil,lanes.cancel_error) = linda:set(key_num|str|bool|lightuserdata [, value [, ...]]) |
| 811 | * | 813 | * |
| 812 | * Set one or more value to Linda. Ignores limits. | 814 | * Set one or more value to Linda. Ignores limits. |
| 813 | * | 815 | * |
| @@ -827,13 +829,13 @@ LUAG_FUNC(linda_set) | |||
| 827 | Keeper* const _keeper{ _linda->whichKeeper() }; | 829 | Keeper* const _keeper{ _linda->whichKeeper() }; |
| 828 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, 2); | 830 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, 2); |
| 829 | if (_pushed.has_value()) { // no error? | 831 | if (_pushed.has_value()) { // no error? |
| 830 | LUA_ASSERT(L_, _pushed.value() == 1 && luaG_type(L_, -1) == LuaType::BOOLEAN); | 832 | LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, -1) == LuaType::STRING && luaG_type(L_, -2) == LuaType::BOOLEAN); |
| 831 | 833 | ||
| 832 | if (_has_data) { | 834 | if (_has_data) { |
| 833 | // we put some data in the slot, tell readers that they should wake | 835 | // we put some data in the slot, tell readers that they should wake |
| 834 | _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area | 836 | _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area |
| 835 | } | 837 | } |
| 836 | if (lua_toboolean(L_, -1)) { | 838 | if (lua_toboolean(L_, -2)) { |
| 837 | // the key was full, but it is no longer the case, tell writers they should wake | 839 | // the key was full, but it is no longer the case, tell writers they should wake |
| 838 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | 840 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area |
| 839 | } | 841 | } |
