diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-12 18:18:24 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-12 18:18:24 +0200 |
commit | dddc28153796f9c8eb256eddb335c8643226fd0b (patch) | |
tree | 641caa9a01933d0397a99f127cff249d3a77fdb5 | |
parent | c305ff3ed1f51d86ced2bb83b10b5e0632cd98a3 (diff) | |
download | lanes-dddc28153796f9c8eb256eddb335c8643226fd0b.tar.gz lanes-dddc28153796f9c8eb256eddb335c8643226fd0b.tar.bz2 lanes-dddc28153796f9c8eb256eddb335c8643226fd0b.zip |
linda :get(), :set(), :limit() return value changes
-rw-r--r-- | CHANGES | 3 | ||||
-rw-r--r-- | docs/index.html | 15 | ||||
-rw-r--r-- | src/keeper.cpp | 47 | ||||
-rw-r--r-- | src/lanes.lua | 41 | ||||
-rw-r--r-- | src/linda.cpp | 38 | ||||
-rw-r--r-- | tests/basic.lua | 24 | ||||
-rw-r--r-- | tests/cancel.lua | 7 | ||||
-rw-r--r-- | tests/errhangtest.lua | 12 | ||||
-rw-r--r-- | tests/keeper.lua | 5 | ||||
-rw-r--r-- | tests/linda_perf.lua | 10 | ||||
-rw-r--r-- | tests/timer.lua | 7 | ||||
-rw-r--r-- | tests/tobeclosed.lua | 13 |
12 files changed, 129 insertions, 93 deletions
@@ -18,13 +18,14 @@ CHANGE 2: BGe 11-Jun-24 | |||
18 | - new function lanes.finally(). Installs a function that gets called at Lanes shutdown. | 18 | - new function lanes.finally(). Installs a function that gets called at Lanes shutdown. |
19 | - lanes have a __close metamethod that calls join(). | 19 | - lanes have a __close metamethod that calls join(). |
20 | - lanes can no longer be "killed" by hard-stopping their thread without any resource cleanup (see lane:cancel()). | 20 | - lanes can no longer be "killed" by hard-stopping their thread without any resource cleanup (see lane:cancel()). |
21 | - lane:join(), linda:receive(), linda:send() return nil, error in case of problem. | 21 | - lane:join() returns nil, error in case of problem. |
22 | - lane function body must return a non-nil first value on success if lane is waited upon with lane:join(). | 22 | - lane function body must return a non-nil first value on success if lane is waited upon with lane:join(). |
23 | - lanes.sleep() accept a new argument "indefinitely" to block forever (until hard cancellation is received). | 23 | - lanes.sleep() accept a new argument "indefinitely" to block forever (until hard cancellation is received). |
24 | - Lindas: | 24 | - Lindas: |
25 | - providing "auto" as name when constructing a Linda cause Lanes to provide a name built from the source location of the construction. | 25 | - providing "auto" as name when constructing a Linda cause Lanes to provide a name built from the source location of the construction. |
26 | - specifying a group to lanes.linda() is mandatory when Lanes is configured with user Keepers. | 26 | - specifying a group to lanes.linda() is mandatory when Lanes is configured with user Keepers. |
27 | - linda:deep() result no longer contains the raw C pointer of the Linda object. | 27 | - linda:deep() result no longer contains the raw C pointer of the Linda object. |
28 | - linda :receive(), :send(), :get(), :set(), :limit() return nil, error in case of problem. Returned values in case of success change too. | ||
28 | - Lindas have a __close metamethod that calls any provided suitable handler at Linda creation. | 29 | - Lindas have a __close metamethod that calls any provided suitable handler at Linda creation. |
29 | - Lane generator settings: | 30 | - Lane generator settings: |
30 | - error_trace_level added. Replaces the global verbose_errors setting. | 31 | - error_trace_level added. Replaces the global verbose_errors setting. |
diff --git a/docs/index.html b/docs/index.html index e884145..06ce610 100644 --- a/docs/index.html +++ b/docs/index.html | |||
@@ -1209,13 +1209,14 @@ | |||
1209 | </p> | 1209 | </p> |
1210 | 1210 | ||
1211 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1211 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1212 | true|(nil,[lanes.cancel_error|"timeout"]) = h:limit(key, n_uint) | 1212 | bool|(nil,[lanes.cancel_error|"timeout"]) = h:limit(key, n_uint) |
1213 | </pre></td></tr></table> | 1213 | </pre></td></tr></table> |
1214 | 1214 | ||
1215 | <p> | 1215 | <p> |
1216 | 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. <tt>nil</tt> removes the limit.<br /> | 1216 | 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. <tt>nil</tt> removes the limit.<br /> |
1217 | A limit of 0 is allowed to block everything.<br /> | 1217 | A limit of 0 is allowed to block everything.<br /> |
1218 | 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.<br /> | 1218 | 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>.<br /> |
1219 | Or <tt>nil, lanes.cancel_error</tt> in case of cancellation, of course. | ||
1219 | </p> | 1220 | </p> |
1220 | 1221 | ||
1221 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1222 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
@@ -1276,15 +1277,19 @@ | |||
1276 | </p> | 1277 | </p> |
1277 | 1278 | ||
1278 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> | 1279 | <table border="1" bgcolor="#E0E0FF" cellpadding="10" style="width:50%"><tr><td><pre> |
1279 | true|lanes.cancel_error = linda_h:set(key [, val [, ...]]) | 1280 | true|nil,lanes.cancel_error = linda_h:set(key [, val [, ...]]) |
1280 | 1281 | ||
1281 | [[val [, ...]]|lanes.cancel_error] = linda_h:get(key [, count = 1]) | 1282 | [number,[val [, ...]]|nil,lanes.cancel_error] = linda_h:get(key [, count = 1]) |
1282 | </pre></td></tr></table> | 1283 | </pre></td></tr></table> |
1283 | 1284 | ||
1284 | <p> | 1285 | <p> |
1285 | 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 /> | 1286 | 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 /> |
1286 | Writing to a slot never blocks because it ignores the limit. It overwrites existing value and clears any possible queued entries.<br /> | 1287 | Writing to a slot never blocks because it ignores the limit. It overwrites existing value and clears any possible queued entries.<br /> |
1287 | Reading doesn't block either because <tt>get()</tt> returns whatever is available (which can be nothing), up to the specified count.<br /> | 1288 | Reading doesn't block either because <tt>get()</tt> returns: |
1289 | <ul> | ||
1290 | <li><tt>nil, lanes.cancel_error</tt> in case of cancellation.</li> | ||
1291 | <li><tt>number, val...</tt> where number is the actual count of items obtained from the linda (can be 0).</li> | ||
1292 | </ul> | ||
1288 | 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. | 1293 | 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. |
1289 | </p> | 1294 | </p> |
1290 | 1295 | ||
diff --git a/src/keeper.cpp b/src/keeper.cpp index 7000372..4175d84 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
@@ -123,26 +123,32 @@ KeyUD* KeyUD::GetPtr(KeeperState const K_, int idx_) | |||
123 | // ################################################################################################# | 123 | // ################################################################################################# |
124 | 124 | ||
125 | // in: fifo | 125 | // in: fifo |
126 | // out: ...|nothing | 126 | // out: bool ... |
127 | // pops the fifo, push as much data as is available (up to the specified count) without consuming it | 127 | // pops the fifo, push bool + as much data as is available (up to the specified count) without consuming it |
128 | // bool is true if the requested count was served, else false | ||
128 | void KeyUD::peek(KeeperState const K_, int const count_) | 129 | void KeyUD::peek(KeeperState const K_, int const count_) |
129 | { | 130 | { |
130 | LUA_ASSERT(K_, KeyUD::GetPtr(K_, -1) == this); | 131 | STACK_CHECK_START_ABS(K_, 1); |
132 | LUA_ASSERT(K_, KeyUD::GetPtr(K_, -1) == this); // K_: KeyUD | ||
131 | if (count <= 0) { // no data is available | 133 | if (count <= 0) { // no data is available |
132 | lua_pop(K_, 1); // K_: | 134 | lua_pop(K_, 1); // K_: |
135 | lua_pushinteger(K_, 0); // K_: 0 | ||
133 | return; | 136 | return; |
134 | } | 137 | } |
135 | 138 | ||
136 | // read <count_> value off the fifo | 139 | // read <count_> value off the fifo, if possible |
137 | prepareAccess(K_, -1); // K_: fifo | 140 | prepareAccess(K_, -1); // K_: fifo |
138 | int const _at{ lua_gettop(K_) }; | ||
139 | int const _count{ std::min(count_, count) }; | 141 | int const _count{ std::min(count_, count) }; |
142 | lua_pushinteger(K_, _count); // K_: fifo _count | ||
143 | lua_insert(K_, 1); // K_: _count fifo | ||
144 | STACK_CHECK(K_, 2); | ||
140 | STACK_GROW(K_, _count); | 145 | STACK_GROW(K_, _count); |
141 | for (int const _i : std::ranges::iota_view{ 1, _count }) { // push val2 to valN | 146 | for (int const _i : std::ranges::iota_view{ 1, _count }) { // push val2 to valN |
142 | lua_rawgeti(K_, 1, first + _i); // K_: fifo val2..N | 147 | lua_rawgeti(K_, 2, first + _i); // K_: _count fifo val2..N |
143 | } | 148 | } |
144 | lua_rawgeti(K_, 1, first); // push val1 // K_: fifo val2..N val1 | 149 | lua_rawgeti(K_, 2, first); // push val1 // K_: _count fifo val2..N val1 |
145 | lua_replace(K_, _at); // replace fifo by val1 to get the output properly ordered // K_: val1..N | 150 | lua_replace(K_, 2); // replace fifo by val1 to get the output properly ordered // K_: _count val1..N |
151 | STACK_CHECK(K_, 1 + _count); | ||
146 | } | 152 | } |
147 | 153 | ||
148 | // ################################################################################################# | 154 | // ################################################################################################# |
@@ -418,7 +424,7 @@ int keepercall_destruct(lua_State* const L_) | |||
418 | // ################################################################################################# | 424 | // ################################################################################################# |
419 | 425 | ||
420 | // in: linda_ud key [count] | 426 | // in: linda_ud key [count] |
421 | // out: at most <count> values | 427 | // out: bool + at most <count> values |
422 | int keepercall_get(lua_State* const L_) | 428 | int keepercall_get(lua_State* const L_) |
423 | { | 429 | { |
424 | KeeperState const _K{ L_ }; | 430 | KeeperState const _K{ L_ }; |
@@ -433,11 +439,13 @@ int keepercall_get(lua_State* const L_) | |||
433 | lua_remove(_K, 1); // _K: KeyUD | 439 | lua_remove(_K, 1); // _K: KeyUD |
434 | KeyUD* const _key{ KeyUD::GetPtr(_K, -1) }; | 440 | KeyUD* const _key{ KeyUD::GetPtr(_K, -1) }; |
435 | if (_key != nullptr) { | 441 | if (_key != nullptr) { |
436 | _key->peek(_K, _count); // _K: val... | 442 | _key->peek(_K, _count); // _K: N val... |
437 | } else { | 443 | } else { |
438 | // no fifo was ever registered for this key, or it is empty | 444 | // no fifo was ever registered for this key, or it is empty |
439 | lua_pop(_K, 1); // _K: | 445 | lua_pop(_K, 1); // _K: |
446 | lua_pushinteger(_K, 0); // _K: 0 | ||
440 | } | 447 | } |
448 | LUA_ASSERT(_K, lua_isnumber(_K, 1)); | ||
441 | return lua_gettop(_K); | 449 | return lua_gettop(_K); |
442 | } | 450 | } |
443 | 451 | ||
@@ -462,13 +470,10 @@ int keepercall_limit(lua_State* const L_) | |||
462 | } | 470 | } |
463 | // remove any clutter on the stack | 471 | // remove any clutter on the stack |
464 | lua_settop(_K, 0); // _K: | 472 | lua_settop(_K, 0); // _K: |
465 | if (_key->changeLimit(_limit)) { | 473 | // return true if we decide that blocked threads waiting to write on that key should be awakened |
466 | // return true if we decide that blocked threads waiting to write on that key should be awakened | 474 | // this is the case if we detect the key was full but it is no longer the case |
467 | // this is the case if we detect the key was full but it is no longer the case | 475 | lua_pushboolean(_K, _key->changeLimit(_limit) ? 1 : 0); // _K: bool |
468 | lua_pushboolean(_K, 1); // _K: true | 476 | return 1; |
469 | } | ||
470 | // return 0 or 1 value | ||
471 | return lua_gettop(_K); | ||
472 | } | 477 | } |
473 | 478 | ||
474 | // ################################################################################################# | 479 | // ################################################################################################# |
@@ -570,7 +575,7 @@ int keepercall_send(lua_State* const L_) | |||
570 | // ################################################################################################# | 575 | // ################################################################################################# |
571 | 576 | ||
572 | // in: linda key [val...] | 577 | // in: linda key [val...] |
573 | // out: true if the linda was full but it's no longer the case, else nothing | 578 | // out: true if the linda was full but it's no longer the case, else false |
574 | int keepercall_set(lua_State* const L_) | 579 | int keepercall_set(lua_State* const L_) |
575 | { | 580 | { |
576 | KeeperState const _K{ L_ }; | 581 | KeeperState const _K{ L_ }; |
@@ -593,7 +598,7 @@ int keepercall_set(lua_State* const L_) | |||
593 | lua_pushnil(_K); // _K: KeysDB key nil | 598 | lua_pushnil(_K); // _K: KeysDB key nil |
594 | lua_rawset(_K, -3); // _K: KeysDB | 599 | lua_rawset(_K, -3); // _K: KeysDB |
595 | } else { | 600 | } else { |
596 | lua_remove(_K, -2); // _K: KeysDB KeyUD | 601 | lua_remove(_K, -2); // KeyUD::reset expects KeyUD at the top // _K: KeysDB KeyUD |
597 | // we create room if the KeyUD was full but it is no longer the case | 602 | // we create room if the KeyUD was full but it is no longer the case |
598 | _should_wake_writers = _key->reset(_K); | 603 | _should_wake_writers = _key->reset(_K); |
599 | } | 604 | } |
@@ -619,7 +624,9 @@ int keepercall_set(lua_State* const L_) | |||
619 | lua_replace(_K, -2 - _count); // _K: KeysDB KeyUD val... | 624 | lua_replace(_K, -2 - _count); // _K: KeysDB KeyUD val... |
620 | [[maybe_unused]] bool const _pushed{ _key->push(_K, _count) }; // _K: KeysDB | 625 | [[maybe_unused]] bool const _pushed{ _key->push(_K, _count) }; // _K: KeysDB |
621 | } | 626 | } |
622 | return _should_wake_writers ? (lua_pushboolean(_K, 1), 1) : 0; | 627 | // stack isn't the same here depending on what we did before, but that's not a problem |
628 | lua_pushboolean(_K, _should_wake_writers ? 1 : 0); // _K: ... bool | ||
629 | return 1; | ||
623 | } | 630 | } |
624 | 631 | ||
625 | // ################################################################################################# | 632 | // ################################################################################################# |
diff --git a/src/lanes.lua b/src/lanes.lua index d890adf..c860ba0 100644 --- a/src/lanes.lua +++ b/src/lanes.lua | |||
@@ -400,7 +400,8 @@ local configure_timers = function() | |||
400 | 400 | ||
401 | -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally has 'table' always declared) | 401 | -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally has 'table' always declared) |
402 | local first_time_key = "first time" | 402 | local first_time_key = "first time" |
403 | local first_time = timerLinda:get(first_time_key) == nil | 403 | local _, _first_time_val = timerLinda:get(first_time_key) |
404 | local first_time = (_first_time_val == nil) | ||
404 | timerLinda:set(first_time_key, true) | 405 | timerLinda:set(first_time_key, true) |
405 | if first_time then | 406 | if first_time then |
406 | 407 | ||
@@ -490,8 +491,8 @@ local configure_timers = function() | |||
490 | -- | 491 | -- |
491 | local t2 = t1[key_] | 492 | local t2 = t1[key_] |
492 | if not t2 then | 493 | if not t2 then |
493 | t2= {} | 494 | t2 = {} |
494 | t1[key_]= t2 | 495 | t1[key_] = t2 |
495 | end | 496 | end |
496 | 497 | ||
497 | t2[1] = wakeup_at_ | 498 | t2[1] = wakeup_at_ |
@@ -507,10 +508,10 @@ local configure_timers = function() | |||
507 | local now = now_secs() | 508 | local now = now_secs() |
508 | local next_wakeup | 509 | local next_wakeup |
509 | 510 | ||
510 | for linda_deep,t1 in pairs(collection) do | 511 | for linda_deep, t1 in pairs(collection) do |
511 | for key,t2 in pairs(t1) do | 512 | for key, t2 in pairs(t1) do |
512 | -- | 513 | -- |
513 | if key==linda_deep then | 514 | if key == linda_deep then |
514 | -- no 'continue' in Lua :/ | 515 | -- no 'continue' in Lua :/ |
515 | else | 516 | else |
516 | -- 't2': { wakeup_at_secs [,period_secs] } | 517 | -- 't2': { wakeup_at_secs [,period_secs] } |
@@ -519,10 +520,10 @@ local configure_timers = function() | |||
519 | local period= t2[2] -- may be 'nil' | 520 | local period= t2[2] -- may be 'nil' |
520 | 521 | ||
521 | if wakeup_at <= now then | 522 | if wakeup_at <= now then |
522 | local linda= t1[linda_deep] | 523 | local linda = t1[linda_deep] |
523 | assert(linda) | 524 | assert(linda) |
524 | 525 | ||
525 | linda:set(key, now ) | 526 | linda:set(key, now) |
526 | 527 | ||
527 | -- 'pairs()' allows the values to be modified (and even | 528 | -- 'pairs()' allows the values to be modified (and even |
528 | -- removed) as far as keys are not touched | 529 | -- removed) as far as keys are not touched |
@@ -530,13 +531,13 @@ local configure_timers = function() | |||
530 | if not period then | 531 | if not period then |
531 | -- one-time timer; gone | 532 | -- one-time timer; gone |
532 | -- | 533 | -- |
533 | t1[key]= nil | 534 | t1[key] = nil |
534 | wakeup_at= nil -- no 'continue' in Lua :/ | 535 | wakeup_at = nil -- no 'continue' in Lua :/ |
535 | else | 536 | else |
536 | -- repeating timer; find next wakeup (may jump multiple repeats) | 537 | -- repeating timer; find next wakeup (may jump multiple repeats) |
537 | -- | 538 | -- |
538 | repeat | 539 | repeat |
539 | wakeup_at= wakeup_at+period | 540 | wakeup_at= wakeup_at+period |
540 | until wakeup_at > now | 541 | until wakeup_at > now |
541 | 542 | ||
542 | t2[1]= wakeup_at | 543 | t2[1]= wakeup_at |
@@ -544,7 +545,7 @@ local configure_timers = function() | |||
544 | end | 545 | end |
545 | 546 | ||
546 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then | 547 | if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then |
547 | next_wakeup= wakeup_at | 548 | next_wakeup = wakeup_at |
548 | end | 549 | end |
549 | end | 550 | end |
550 | end -- t2 loop | 551 | end -- t2 loop |
@@ -675,7 +676,12 @@ local cancel_error | |||
675 | local genlock = function(linda_, key_, N) | 676 | local genlock = function(linda_, key_, N) |
676 | -- clear existing data and set the limit | 677 | -- clear existing data and set the limit |
677 | N = N or 1 | 678 | N = N or 1 |
678 | if linda_:set(key_) == cancel_error or linda_:limit(key_, N) == cancel_error then | 679 | local _status, _err = linda_:set(key_) |
680 | if _err == cancel_error then | ||
681 | return cancel_error | ||
682 | end | ||
683 | local _status, _err = linda_:limit(key_, N) | ||
684 | if _err == cancel_error then | ||
679 | return cancel_error | 685 | return cancel_error |
680 | end | 686 | end |
681 | 687 | ||
@@ -723,7 +729,12 @@ end -- genlock | |||
723 | -- | 729 | -- |
724 | local genatomic = function(linda_, key_, initial_val_) | 730 | local genatomic = function(linda_, key_, initial_val_) |
725 | -- clears existing data (also queue). the slot may contain the stored value, and an additional boolean value | 731 | -- clears existing data (also queue). the slot may contain the stored value, and an additional boolean value |
726 | if linda_:limit(key_, 2) == cancel_error or linda_:set(key_, initial_val_ or 0.0) == cancel_error then | 732 | local _status, _err = linda_:limit(key_, 2) |
733 | if _err == cancel_error then | ||
734 | return cancel_error | ||
735 | end | ||
736 | local _status, _err = linda_:set(key_, initial_val_ or 0.0) | ||
737 | if _err == cancel_error then | ||
727 | return cancel_error | 738 | return cancel_error |
728 | end | 739 | end |
729 | 740 | ||
@@ -734,7 +745,7 @@ local genatomic = function(linda_, key_, initial_val_) | |||
734 | if _err == cancel_error then | 745 | if _err == cancel_error then |
735 | return cancel_error | 746 | return cancel_error |
736 | end | 747 | end |
737 | local val = linda_:get(key_) | 748 | local _, val = linda_:get(key_) |
738 | if val ~= cancel_error then | 749 | if val ~= cancel_error then |
739 | val = val + (diff_ or 1.0) | 750 | val = val + (diff_ or 1.0) |
740 | -- set() releases the lock by emptying queue | 751 | -- set() releases the lock by emptying queue |
diff --git a/src/linda.cpp b/src/linda.cpp index 91138c5..316e917 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
@@ -393,7 +393,7 @@ LUAG_FUNC(linda_dump) | |||
393 | // ################################################################################################# | 393 | // ################################################################################################# |
394 | 394 | ||
395 | /* | 395 | /* |
396 | * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) | 396 | * count, [val [, ...]]|nil,cancel_error = linda:get(key_num|str|bool|lightuserdata [, count = 1]) |
397 | * | 397 | * |
398 | * Get one or more values from Linda. | 398 | * Get one or more values from Linda. |
399 | */ | 399 | */ |
@@ -412,9 +412,10 @@ LUAG_FUNC(linda_get) | |||
412 | Keeper* const _keeper{ _linda->whichKeeper() }; | 412 | Keeper* const _keeper{ _linda->whichKeeper() }; |
413 | _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, 2); | 413 | _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, 2); |
414 | } else { // linda is cancelled | 414 | } else { // linda is cancelled |
415 | // do nothing and return lanes.cancel_error | 415 | // do nothing and return nil,lanes.cancel_error |
416 | lua_pushnil(L_); | ||
416 | kCancelError.pushKey(L_); | 417 | kCancelError.pushKey(L_); |
417 | _pushed.emplace(1); | 418 | _pushed.emplace(2); |
418 | } | 419 | } |
419 | // an error can be raised if we attempt to read an unregistered function | 420 | // an error can be raised if we attempt to read an unregistered function |
420 | return OptionalValue(_pushed, L_, "tried to copy unsupported types"); | 421 | return OptionalValue(_pushed, L_, "tried to copy unsupported types"); |
@@ -425,7 +426,7 @@ LUAG_FUNC(linda_get) | |||
425 | // ################################################################################################# | 426 | // ################################################################################################# |
426 | 427 | ||
427 | /* | 428 | /* |
428 | * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, [int]) | 429 | * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int]) |
429 | * | 430 | * |
430 | * Set limit to 1 Linda keys. | 431 | * Set limit to 1 Linda keys. |
431 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so | 432 | * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so |
@@ -450,15 +451,15 @@ LUAG_FUNC(linda_limit) | |||
450 | if (_linda->cancelRequest == CancelRequest::None) { | 451 | if (_linda->cancelRequest == CancelRequest::None) { |
451 | Keeper* const _keeper{ _linda->whichKeeper() }; | 452 | Keeper* const _keeper{ _linda->whichKeeper() }; |
452 | _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2); | 453 | _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, 2); |
453 | LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 0 || _pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads | 454 | 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 |
454 | if (_pushed.value() == 1) { | 455 | if (lua_toboolean(L_, -1)) { |
455 | LUA_ASSERT(L_, luaG_type(L_, -1) == LuaType::BOOLEAN && lua_toboolean(L_, -1) == 1); | ||
456 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | 456 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area |
457 | } | 457 | } |
458 | } else { // linda is cancelled | 458 | } else { // linda is cancelled |
459 | // do nothing and return lanes.cancel_error | 459 | // do nothing and return nil,lanes.cancel_error |
460 | lua_pushnil(L_); | ||
460 | kCancelError.pushKey(L_); | 461 | kCancelError.pushKey(L_); |
461 | _pushed.emplace(1); | 462 | _pushed.emplace(2); |
462 | } | 463 | } |
463 | // propagate pushed boolean if any | 464 | // propagate pushed boolean if any |
464 | return _pushed.value(); | 465 | return _pushed.value(); |
@@ -470,14 +471,13 @@ LUAG_FUNC(linda_limit) | |||
470 | 471 | ||
471 | /* | 472 | /* |
472 | * 2 modes of operation | 473 | * 2 modes of operation |
473 | * [val, key]= linda_receive( linda_ud, [timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) | 474 | * [val, key]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) |
474 | * Consumes a single value from the Linda, in any key. | 475 | * Consumes a single value from the Linda, in any key. |
475 | * Returns: received value (which is consumed from the slot), and the key which had it | 476 | * Returns: received value (which is consumed from the slot), and the key which had it |
476 | 477 | ||
477 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) | 478 | * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) |
478 | * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. | 479 | * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. |
479 | * returns the actual consumed values, or nil if there weren't enough values to consume | 480 | * returns the actual consumed values, or nil if there weren't enough values to consume |
480 | * | ||
481 | */ | 481 | */ |
482 | LUAG_FUNC(linda_receive) | 482 | LUAG_FUNC(linda_receive) |
483 | { | 483 | { |
@@ -763,7 +763,7 @@ LUAG_FUNC(linda_send) | |||
763 | // ################################################################################################# | 763 | // ################################################################################################# |
764 | 764 | ||
765 | /* | 765 | /* |
766 | * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) | 766 | * [true|nil,lanes.cancel_error] = linda:set(key_num|str|bool|lightuserdata [, value [, ...]]) |
767 | * | 767 | * |
768 | * Set one or more value to Linda. Ignores limits. | 768 | * Set one or more value to Linda. Ignores limits. |
769 | * | 769 | * |
@@ -773,7 +773,7 @@ LUAG_FUNC(linda_set) | |||
773 | { | 773 | { |
774 | auto set = [](lua_State* L_) { | 774 | auto set = [](lua_State* L_) { |
775 | Linda* const _linda{ ToLinda<false>(L_, 1) }; | 775 | Linda* const _linda{ ToLinda<false>(L_, 1) }; |
776 | bool const _has_value{ lua_gettop(L_) > 2 }; | 776 | bool const _has_data{ lua_gettop(L_) > 2 }; |
777 | // make sure the key is of a valid type (throws an error if not the case) | 777 | // make sure the key is of a valid type (throws an error if not the case) |
778 | check_key_types(L_, 2, 2); | 778 | check_key_types(L_, 2, 2); |
779 | 779 | ||
@@ -782,22 +782,22 @@ LUAG_FUNC(linda_set) | |||
782 | if (_linda->cancelRequest == CancelRequest::None) { | 782 | if (_linda->cancelRequest == CancelRequest::None) { |
783 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, 2); | 783 | _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, 2); |
784 | if (_pushed.has_value()) { // no error? | 784 | if (_pushed.has_value()) { // no error? |
785 | LUA_ASSERT(L_, _pushed.value() == 0 || _pushed.value() == 1); | 785 | LUA_ASSERT(L_, _pushed.value() == 1 && luaG_type(L_, -1) == LuaType::BOOLEAN); |
786 | 786 | ||
787 | if (_has_value) { | 787 | if (_has_data) { |
788 | // we put some data in the slot, tell readers that they should wake | 788 | // we put some data in the slot, tell readers that they should wake |
789 | _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area | 789 | _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area |
790 | } | 790 | } |
791 | if (_pushed.value() == 1) { | 791 | if (lua_toboolean(L_, -1)) { |
792 | // the key was full, but it is no longer the case, tell writers they should wake | 792 | // the key was full, but it is no longer the case, tell writers they should wake |
793 | LUA_ASSERT(L_, luaG_type(L_, -1) == LuaType::BOOLEAN && lua_toboolean(L_, -1) == 1); | ||
794 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area | 793 | _linda->readHappened.notify_all(); // To be done from within the 'K' locking area |
795 | } | 794 | } |
796 | } | 795 | } |
797 | } else { // linda is cancelled | 796 | } else { // linda is cancelled |
798 | // do nothing and return lanes.cancel_error | 797 | // do nothing and return nil,lanes.cancel_error |
798 | lua_pushnil(L_); | ||
799 | kCancelError.pushKey(L_); | 799 | kCancelError.pushKey(L_); |
800 | _pushed.emplace(1); | 800 | _pushed.emplace(2); |
801 | } | 801 | } |
802 | 802 | ||
803 | // must trigger any error after keeper state has been released | 803 | // must trigger any error after keeper state has been released |
diff --git a/tests/basic.lua b/tests/basic.lua index bdad44c..ae8ebd9 100644 --- a/tests/basic.lua +++ b/tests/basic.lua | |||
@@ -252,21 +252,21 @@ assert(type(linda) == "userdata" and tostring(linda) == "Linda: communications") | |||
252 | 252 | ||
253 | WR "test linda:get/set..." | 253 | WR "test linda:get/set..." |
254 | linda:set("<->", "x", "y", "z") | 254 | linda:set("<->", "x", "y", "z") |
255 | local x,y,z = linda:get("<->", 1) | 255 | local b,x,y,z = linda:get("<->", 1) |
256 | assert(x == "x" and y == nil and z == nil) | 256 | assert(b == 1 and x == "x" and y == nil and z == nil) |
257 | local x,y,z = linda:get("<->", 2) | 257 | local b,x,y,z = linda:get("<->", 2) |
258 | assert(x == "x" and y == "y" and z == nil) | 258 | assert(b == 2 and x == "x" and y == "y" and z == nil) |
259 | local x,y,z = linda:get("<->", 3) | 259 | local b,x,y,z = linda:get("<->", 3) |
260 | assert(x == "x" and y == "y" and z == "z") | 260 | assert(b == 3 and x == "x" and y == "y" and z == "z") |
261 | local x,y,z,w = linda:get("<->", 4) | 261 | local b,x,y,z,w = linda:get("<->", 4) |
262 | assert(x == "x" and y == "y" and z == "z" and w == nil) | 262 | assert(b == 3 and x == "x" and y == "y" and z == "z" and w == nil) |
263 | local k, x = linda:receive("<->") | 263 | local k, x = linda:receive("<->") |
264 | assert(k == "<->" and x == "x") | 264 | assert(k == "<->" and x == "x") |
265 | local k,y,z = linda:receive(linda.batched, "<->", 2) | 265 | local k,y,z = linda:receive(linda.batched, "<->", 2) |
266 | assert(k == "<->" and y == "y" and z == "z") | 266 | assert(k == "<->" and y == "y" and z == "z") |
267 | linda:set("<->") | 267 | linda:set("<->") |
268 | local x,y,z,w = linda:get("<->", 4) | 268 | local b,x,y,z,w = linda:get("<->", 4) |
269 | assert(x == nil and y == nil and z == nil and w == nil) | 269 | assert(b == 0 and x == nil and y == nil and z == nil and w == nil) |
270 | WR "ok\n" | 270 | WR "ok\n" |
271 | 271 | ||
272 | local function PEEK(...) return linda:get("<-", ...) end | 272 | local function PEEK(...) return linda:get("<-", ...) end |
@@ -282,7 +282,7 @@ SEND(setmetatable({"should be ignored"},{__lanesconvert=lanes.null})); WR("main | |||
282 | for i=1,40 do | 282 | for i=1,40 do |
283 | WR "." | 283 | WR "." |
284 | SLEEP(0.0001) | 284 | SLEEP(0.0001) |
285 | assert(PEEK() == nil) -- nothing coming in, yet | 285 | assert(PEEK() == 0) -- nothing coming in, yet |
286 | end | 286 | end |
287 | SEND(nil); WR("\nmain ", "nil sent\n") | 287 | SEND(nil); WR("\nmain ", "nil sent\n") |
288 | 288 | ||
@@ -306,7 +306,7 @@ assert(null==nil) | |||
306 | local out_t = RECEIVE(); WR(type(out_t).." received\n") | 306 | local out_t = RECEIVE(); WR(type(out_t).." received\n") |
307 | assert(tables_match(out_t, {'a','b','c',d=10})) | 307 | assert(tables_match(out_t, {'a','b','c',d=10})) |
308 | 308 | ||
309 | assert(PEEK() == nil) | 309 | assert(PEEK() == 0) |
310 | SEND(4) | 310 | SEND(4) |
311 | 311 | ||
312 | local complex_table = RECEIVE(); WR(type(complex_table).." received\n") | 312 | local complex_table = RECEIVE(); WR(type(complex_table).." received\n") |
diff --git a/tests/cancel.lua b/tests/cancel.lua index 42ae839..d6c293d 100644 --- a/tests/cancel.lua +++ b/tests/cancel.lua | |||
@@ -38,7 +38,8 @@ if not next(which_tests) or which_tests.genlock then | |||
38 | 38 | ||
39 | -- check that cancelled lindas give cancel_error as they should | 39 | -- check that cancelled lindas give cancel_error as they should |
40 | linda:cancel() | 40 | linda:cancel() |
41 | assert( linda:get( "empty") == lanes.cancel_error) | 41 | local _status, _err = linda:get( "empty") |
42 | assert(_status == nil and _err == lanes.cancel_error) | ||
42 | assert( lanes.genlock( linda, "any", 1) == lanes.cancel_error) | 43 | assert( lanes.genlock( linda, "any", 1) == lanes.cancel_error) |
43 | assert( lanes.genatomic( linda, "any") == lanes.cancel_error) | 44 | assert( lanes.genatomic( linda, "any") == lanes.cancel_error) |
44 | 45 | ||
@@ -102,14 +103,14 @@ local laneBody = function( mode_, payload_) | |||
102 | io.stdout:write( " lane busy waiting ... ") | 103 | io.stdout:write( " lane busy waiting ... ") |
103 | for i = 1, payload_ do | 104 | for i = 1, payload_ do |
104 | -- force a non-jitable call | 105 | -- force a non-jitable call |
105 | local a = linda:get( "val") | 106 | local _, a = linda:get( "val") |
106 | a = a * 2 | 107 | a = a * 2 |
107 | end | 108 | end |
108 | print( "again?") | 109 | print( "again?") |
109 | elseif mode_ == "busy" then | 110 | elseif mode_ == "busy" then |
110 | -- busy wait mode in pure Lua code | 111 | -- busy wait mode in pure Lua code |
111 | io.stdout:write( " lane busy waiting ... ") | 112 | io.stdout:write( " lane busy waiting ... ") |
112 | local a = linda:get( "val") | 113 | local _, a = linda:get( "val") |
113 | for i = 1, payload_ do | 114 | for i = 1, payload_ do |
114 | a = a * 2 | 115 | a = a * 2 |
115 | a = math.sin( a) * math.sin( a) + math.cos( a) * math.cos( a) -- aka 1 | 116 | a = math.sin( a) * math.sin( a) + math.cos( a) * math.cos( a) -- aka 1 |
diff --git a/tests/errhangtest.lua b/tests/errhangtest.lua index d0ffcc4..fff0dee 100644 --- a/tests/errhangtest.lua +++ b/tests/errhangtest.lua | |||
@@ -11,7 +11,8 @@ if true then | |||
11 | print "#### coro set" | 11 | print "#### coro set" |
12 | local coro = coroutine.create(function() end) | 12 | local coro = coroutine.create(function() end) |
13 | print(pcall(linda.set, linda, 'test', coro)) | 13 | print(pcall(linda.set, linda, 'test', coro)) |
14 | assert(linda:get("test") == nil) | 14 | local _count, _val = linda:get("test") |
15 | assert(_count == 0 and _val == nil) | ||
15 | print "OK" | 16 | print "OK" |
16 | end | 17 | end |
17 | 18 | ||
@@ -19,7 +20,8 @@ if true then | |||
19 | print "\n#### reserved sentinels" | 20 | print "\n#### reserved sentinels" |
20 | print(pcall(linda.set, linda, lanes.cancel_error)) | 21 | print(pcall(linda.set, linda, lanes.cancel_error)) |
21 | print(pcall(linda.set, linda, linda.batched)) | 22 | print(pcall(linda.set, linda, linda.batched)) |
22 | assert(linda:get("test") == nil) | 23 | local _count, _val = linda:get("test") |
24 | assert(_count == 0 and _val == nil) | ||
23 | print "OK" | 25 | print "OK" |
24 | end | 26 | end |
25 | 27 | ||
@@ -33,7 +35,8 @@ if true then | |||
33 | print(pcall(linda.set, linda, 'test', true, nil, fun)) | 35 | print(pcall(linda.set, linda, 'test', true, nil, fun)) |
34 | -- read back the contents | 36 | -- read back the contents |
35 | local k,b,n,f = linda:receive(linda.batched, 'test', 3) | 37 | local k,b,n,f = linda:receive(linda.batched, 'test', 3) |
36 | assert(linda:get("test") == nil) | 38 | local _count, _val = linda:get("test") |
39 | assert(_count == 0 and _val == nil) | ||
37 | -- check they are ok | 40 | -- check they are ok |
38 | print(k, b, n) | 41 | print(k, b, n) |
39 | f() | 42 | f() |
@@ -70,7 +73,8 @@ if true then | |||
70 | print "\n#### coro send" | 73 | print "\n#### coro send" |
71 | local coro = coroutine.create(function() end) | 74 | local coro = coroutine.create(function() end) |
72 | print(pcall(linda.send, linda, 'test', coro)) | 75 | print(pcall(linda.send, linda, 'test', coro)) |
73 | assert(linda:get("test") == nil) | 76 | local _count, _val = linda:get("test") |
77 | assert(_count == 0 and _val == nil) | ||
74 | print "OK" | 78 | print "OK" |
75 | end | 79 | end |
76 | 80 | ||
diff --git a/tests/keeper.lua b/tests/keeper.lua index 2f731f0..0e93de2 100644 --- a/tests/keeper.lua +++ b/tests/keeper.lua | |||
@@ -108,8 +108,9 @@ if true then | |||
108 | 108 | ||
109 | local function keeper(linda) | 109 | local function keeper(linda) |
110 | local mt= { | 110 | local mt= { |
111 | __index= function( _, key ) | 111 | __index= function(_, key) |
112 | return linda:get( key ) | 112 | local _count, _val = linda:get(key) |
113 | return _val | ||
113 | end, | 114 | end, |
114 | __newindex= function( _, key, val ) | 115 | __newindex= function( _, key, val ) |
115 | linda:set( key, val ) | 116 | linda:set( key, val ) |
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua index 4b0c005..96f26f4 100644 --- a/tests/linda_perf.lua +++ b/tests/linda_perf.lua | |||
@@ -18,22 +18,22 @@ local finalizer = function(err, stk) | |||
18 | end | 18 | end |
19 | 19 | ||
20 | -- ################################################################################################# | 20 | -- ################################################################################################# |
21 | if false then | 21 | if true then |
22 | do | 22 | do |
23 | print "############################################ tests get/set" | 23 | print "############################################ tests get/set" |
24 | -- linda:get throughput | 24 | -- linda:get throughput |
25 | local l = lanes.linda("get/set") | 25 | local l = lanes.linda("get/set", 1) |
26 | local batch = {} | 26 | local batch = {} |
27 | for i = 1,1000 do | 27 | for i = 1,1000 do |
28 | table.insert(batch, i) | 28 | table.insert(batch, i) |
29 | end | 29 | end |
30 | for _,size in ipairs{1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 } do | 30 | for _,size in ipairs{1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987 } do |
31 | l:set("<->", table_unpack(batch)) | 31 | l:set("<->", table_unpack(batch)) |
32 | local count = math.floor(20000000/size) | 32 | local count = math.floor(20000/math.sqrt(size)) |
33 | print("START", "get("..size..") " .. count, " times") | 33 | print("START", "get("..size..") " .. count, " times") |
34 | local t1 = lanes.now_secs() | 34 | local t1 = lanes.now_secs() |
35 | for i = 1, 2000000/math.sqrt(size) do | 35 | for i = 1, count do |
36 | l:get("<->", size) | 36 | assert (l:get("<->", size) == size) |
37 | end | 37 | end |
38 | print("DURATION = " .. lanes.now_secs() - t1 .. "\n") | 38 | print("DURATION = " .. lanes.now_secs() - t1 .. "\n") |
39 | end | 39 | end |
diff --git a/tests/timer.lua b/tests/timer.lua index 4da1a50..ac8b385 100644 --- a/tests/timer.lua +++ b/tests/timer.lua | |||
@@ -94,8 +94,11 @@ lanes.timer( linda, T2, 0 ) | |||
94 | linda:receive( 0, T1 ) -- clear out; there could be one tick left | 94 | linda:receive( 0, T1 ) -- clear out; there could be one tick left |
95 | linda:receive( 0, T2 ) | 95 | linda:receive( 0, T2 ) |
96 | 96 | ||
97 | assert( linda:get(T1) == nil ) | 97 | local _count, _val = linda:get(T1) |
98 | assert( linda:get(T2) == nil ) | 98 | assert(_count == 0 and _val == nil) |
99 | |||
100 | local _count, _val = linda:get(T2) | ||
101 | assert(_count == 0 and _val == nil) | ||
99 | 102 | ||
100 | PRINT "...making sure no ticks are coming..." | 103 | PRINT "...making sure no ticks are coming..." |
101 | 104 | ||
diff --git a/tests/tobeclosed.lua b/tests/tobeclosed.lua index 6e3de4c..5ac8ab7 100644 --- a/tests/tobeclosed.lua +++ b/tests/tobeclosed.lua | |||
@@ -69,9 +69,10 @@ do | |||
69 | 69 | ||
70 | do | 70 | do |
71 | 71 | ||
72 | local l_out <close> = l:get("trip") | 72 | local _, l_out <close> = l:get("trip") |
73 | end | 73 | end |
74 | assert(l_in:get("closed") == true) | 74 | local _count, _closed = l_in:get("closed") |
75 | assert(_count == 1 and _closed == true) | ||
75 | end | 76 | end |
76 | 77 | ||
77 | -- ################################################################################################# | 78 | -- ################################################################################################# |
@@ -100,7 +101,7 @@ do | |||
100 | local lane_body = function(l_arg_) | 101 | local lane_body = function(l_arg_) |
101 | WR "In lane body" | 102 | WR "In lane body" |
102 | -- linda obtained through a linda | 103 | -- linda obtained through a linda |
103 | local l_out <close> = l:get("trip") | 104 | local _count, l_out <close> = l:get("trip") |
104 | -- linda from arguments | 105 | -- linda from arguments |
105 | local l_arg <close> = l_arg_ | 106 | local l_arg <close> = l_arg_ |
106 | return true | 107 | return true |
@@ -108,7 +109,8 @@ do | |||
108 | 109 | ||
109 | local close_handler_f = function(linda_, err_) | 110 | local close_handler_f = function(linda_, err_) |
110 | WR("f closing ", linda_) | 111 | WR("f closing ", linda_) |
111 | linda_:set("closed", (linda_:get("closed") or 0) + 1) | 112 | local _count, _closed = linda_:get("closed") |
113 | linda_:set("closed", (_closed or 0) + 1) | ||
112 | end | 114 | end |
113 | local l_in = lanes.linda("voyager", close_handler_f) | 115 | local l_in = lanes.linda("voyager", close_handler_f) |
114 | l:set("trip", l_in) | 116 | l:set("trip", l_in) |
@@ -116,7 +118,8 @@ do | |||
116 | do | 118 | do |
117 | lanes.gen("*", lane_body)(l_in):join() | 119 | lanes.gen("*", lane_body)(l_in):join() |
118 | end | 120 | end |
119 | assert(l_in:get("closed") == 2) | 121 | local _count, _closed = l_in:get("closed") |
122 | assert(_count == 1 and _closed == 2) | ||
120 | end | 123 | end |
121 | 124 | ||
122 | WR "================================================================================================" | 125 | WR "================================================================================================" |