diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-27 12:40:36 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-27 12:40:36 +0200 |
| commit | 726aee3fbb909946e69866cc6c4497c5ec365fe8 (patch) | |
| tree | 3d90edeb2a97039b464e7e61e7d87d901319e625 | |
| parent | 62a7eab66f8f6af66c94390138815c3171b62810 (diff) | |
| download | lanes-726aee3fbb909946e69866cc6c4497c5ec365fe8.tar.gz lanes-726aee3fbb909946e69866cc6c4497c5ec365fe8.tar.bz2 lanes-726aee3fbb909946e69866cc6c4497c5ec365fe8.zip | |
linda:limit() and linda:set() return a second value, a string representing the fill status
| -rw-r--r-- | docs/index.html | 37 | ||||
| -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 | ||||
| -rw-r--r-- | tests/atomic.lua | 1 |
6 files changed, 92 insertions, 46 deletions
diff --git a/docs/index.html b/docs/index.html index 8e84fcb..148b5ab 100644 --- a/docs/index.html +++ b/docs/index.html | |||
| @@ -1222,15 +1222,16 @@ | |||
| 1222 | </p> | 1222 | </p> |
| 1223 | 1223 | ||
| 1224 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1224 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
| 1225 | bool|(nil,[lanes.cancel_error|"timeout"]) = h:limit(key, <limit>) | 1225 | bool,string|(nil,[lanes.cancel_error|"timeout"]) = h:limit(key, <limit>) |
| 1226 | >limit< = h:limit(key) | 1226 | (number|string),string = h:limit(key) |
| 1227 | </pre></td></tr></table> | 1227 | </pre></td></tr></table> |
| 1228 | 1228 | ||
| 1229 | <p> | 1229 | <p> |
| 1230 | By default, queue sizes are unlimited but limits can be enforced using the <tt>limit()</tt> method. This can be useful to balance execution speeds in a producer/consumer scenario.<br /> | 1230 | By default, queue sizes are unlimited but limits can be enforced using the <tt>limit()</tt> method. This can be useful to balance execution speeds in a producer/consumer scenario.<br /> |
| 1231 | A limit of 0 is allowed to block everything. <tt>"unlimited"</tt> removes the limit.<br /> | 1231 | A limit of 0 is allowed to block everything. <tt>"unlimited"</tt> removes the limit.<br /> |
| 1232 | If the key was full but the limit change added some room, <tt>limit()</tt> returns <tt>true</tt> and the Linda is signalled so that <tt>send()</tt>-blocked threads are awakened, else the return value is <tt>false</tt>. | 1232 | If the key was full but the limit change added some room, <tt>limit()</tt> first return value is <tt>true</tt> and the Linda is signalled so that <tt>send()</tt>-blocked threads are awakened, else the return value is <tt>false</tt>. |
| 1233 | If no limit is provided, <tt>limit()</tt> returns a single value, the current limit for the specified key.<br /> | 1233 | If no limit is provided, <tt>limit()</tt> first return value is the current limit for the specified key.<br /> |
| 1234 | The second returned value is a string representing the fill status relatively to the key's current limit (one of <tt>"over"</tt>, <tt>"under"</tt>, <tt>"exact"</tt>). | ||
| 1234 | Whether reading or writing, if the Linda is cancelled, <tt>limit()</tt> returns <tt>nil, lanes.cancel_error</tt>. | 1235 | Whether reading or writing, if the Linda is cancelled, <tt>limit()</tt> returns <tt>nil, lanes.cancel_error</tt>. |
| 1235 | </p> | 1236 | </p> |
| 1236 | 1237 | ||
| @@ -1292,30 +1293,30 @@ | |||
| 1292 | </p> | 1293 | </p> |
| 1293 | 1294 | ||
| 1294 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1295 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
| 1295 | true|nil,lanes.cancel_error = linda_h:set(key [, val [, ...]]) | 1296 | (bool,string)|(nil,lanes.cancel_error) = linda_h:set(key [, val [, ...]]) |
| 1296 | 1297 | ||
| 1297 | [number,[val [, ...]]|nil,lanes.cancel_error] = linda_h:get(key [, count = 1]) | 1298 | (number,[val [, ...]])|(nil,lanes.cancel_error) = linda_h:get(key [, count = 1]) |
| 1298 | </pre></td></tr></table> | 1299 | </pre></td></tr></table> |
| 1299 | 1300 | ||
| 1300 | <p> | 1301 | <p> |
| 1301 | The table access methods are for accessing a slot without queuing or consuming. They can be used for making shared tables of storage among the lanes.<br /> | 1302 | <tt>get()</tt>/<tt>set()</tt> and <tt>send()</tt>/<tt>receive()</tt> can be used together; reading a slot essentially peeks the next outcoming value of a queue.<br /> |
| 1302 | Writing to a slot never blocks because it ignores the limit. It overwrites existing value and clears any possible queued entries.<br /> | 1303 | <tt>get()</tt>/<tt>set()</tt> are for accessing a key without queuing or consuming. They can be used for making shared tables of storage among the lanes.<br /> |
| 1303 | Reading doesn't block either because <tt>get()</tt> returns: | 1304 | Writing to a key never blocks because it ignores the limit. It overwrites existing values and clears any possible queued entries.<br /> |
| 1305 | </p> | ||
| 1306 | <p> | ||
| 1307 | <tt>get()</tt> can read several values at once, and does not block. Return values ares: | ||
| 1304 | <ul> | 1308 | <ul> |
| 1305 | <li><tt>nil, lanes.cancel_error</tt> in case of cancellation.</li> | 1309 | <li><tt>nil, lanes.cancel_error</tt> in case of cancellation.</li> |
| 1306 | <li><tt>number, val...</tt> where number is the actual count of items obtained from the linda (can be 0).</li> | 1310 | <li><tt>number, val...</tt> where number is the actual count of items obtained from the linda (can be 0).</li> |
| 1307 | </ul> | 1311 | </ul> |
| 1308 | Table access and <tt>send()</tt>/<tt>receive()</tt> can be used together; reading a slot essentially peeks the next outcoming value of a queue. | ||
| 1309 | </p> | ||
| 1310 | |||
| 1311 | <p> | ||
| 1312 | <tt>set()</tt> signals the Linda for write if a value is stored. If nothing special happens, <tt>set() </tt>returns nothing.<br /> | ||
| 1313 | If the key was full but the new data count of the key after <tt>set()</tt> is below its limit, <tt>set()</tt> returns <tt>true</tt> and the Linda is also signaled for read so that <tt>send()</tt>-blocked threads are awakened. | ||
| 1314 | </p> | 1312 | </p> |
| 1315 | |||
| 1316 | <p> | 1313 | <p> |
| 1317 | <tt>set()</tt> can write several values at the specified key, writing <tt>nil</tt> values is now possible, and clearing the contents at the specified key is done by not providing any value.<br /> | 1314 | <tt>set()</tt> can write several values at the specified key. Writing <tt>nil</tt> values is possible, and clearing the contents at the specified key is done by not providing any value.<br /> |
| 1318 | Also, <tt>get()</tt> can read several values at once. If the key contains no data, <tt>get()</tt> returns no value. This can be used to separate the case when reading stored <tt>nil</tt> values. | 1315 | If <tt>set()</tt> actually stores data, the Linda is signalled for write, so that <tt>receive()</tt>-blocked Lanes are awakened.<br /> |
| 1316 | Clearing the contents of a non-existent key does not create it!<br /> | ||
| 1317 | If the key was full but the new data count of the key after <tt>set()</tt> is below its limit, <tt>set()</tt> first return value is <tt>true</tt> and the Linda is also signaled for read, so that <tt>send()</tt>-blocked Lanes are awakened.<br /> | ||
| 1318 | If the key was not already full, nothing additional happens, and <tt>set()</tt> first return value is <tt>false</tt>.<br /> | ||
| 1319 | The second return value is a string representing the fill status relatively to the key's current limit (one of <tt>"over"</tt>, <tt>"under"</tt>, <tt>"exact"</tt>). | ||
| 1319 | </p> | 1320 | </p> |
| 1320 | 1321 | ||
| 1321 | <p> | 1322 | <p> |
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 | } |
diff --git a/tests/atomic.lua b/tests/atomic.lua index 2de8f52..511a19e 100644 --- a/tests/atomic.lua +++ b/tests/atomic.lua | |||
| @@ -9,6 +9,7 @@ local lanes = require "lanes" | |||
| 9 | local linda= lanes.linda() | 9 | local linda= lanes.linda() |
| 10 | local key= "$" | 10 | local key= "$" |
| 11 | 11 | ||
| 12 | -- TODO: test what happens when we cancel the linda | ||
| 12 | local f= lanes.genatomic( linda, key, 5 ) | 13 | local f= lanes.genatomic( linda, key, 5 ) |
| 13 | 14 | ||
| 14 | local v | 15 | local v |
