aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-06-12 18:18:24 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-06-12 18:18:24 +0200
commitdddc28153796f9c8eb256eddb335c8643226fd0b (patch)
tree641caa9a01933d0397a99f127cff249d3a77fdb5
parentc305ff3ed1f51d86ced2bb83b10b5e0632cd98a3 (diff)
downloadlanes-dddc28153796f9c8eb256eddb335c8643226fd0b.tar.gz
lanes-dddc28153796f9c8eb256eddb335c8643226fd0b.tar.bz2
lanes-dddc28153796f9c8eb256eddb335c8643226fd0b.zip
linda :get(), :set(), :limit() return value changes
-rw-r--r--CHANGES3
-rw-r--r--docs/index.html15
-rw-r--r--src/keeper.cpp47
-rw-r--r--src/lanes.lua41
-rw-r--r--src/linda.cpp38
-rw-r--r--tests/basic.lua24
-rw-r--r--tests/cancel.lua7
-rw-r--r--tests/errhangtest.lua12
-rw-r--r--tests/keeper.lua5
-rw-r--r--tests/linda_perf.lua10
-rw-r--r--tests/timer.lua7
-rw-r--r--tests/tobeclosed.lua13
12 files changed, 129 insertions, 93 deletions
diff --git a/CHANGES b/CHANGES
index 64c8bdf..80e94cf 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
128void KeyUD::peek(KeeperState const K_, int const count_) 129void 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
422int keepercall_get(lua_State* const L_) 428int 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
574int keepercall_set(lua_State* const L_) 579int 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
675local genlock = function(linda_, key_, N) 676local 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--
724local genatomic = function(linda_, key_, initial_val_) 730local 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 */
482LUAG_FUNC(linda_receive) 482LUAG_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
253WR "test linda:get/set..." 253WR "test linda:get/set..."
254linda:set("<->", "x", "y", "z") 254linda:set("<->", "x", "y", "z")
255local x,y,z = linda:get("<->", 1) 255local b,x,y,z = linda:get("<->", 1)
256assert(x == "x" and y == nil and z == nil) 256assert(b == 1 and x == "x" and y == nil and z == nil)
257local x,y,z = linda:get("<->", 2) 257local b,x,y,z = linda:get("<->", 2)
258assert(x == "x" and y == "y" and z == nil) 258assert(b == 2 and x == "x" and y == "y" and z == nil)
259local x,y,z = linda:get("<->", 3) 259local b,x,y,z = linda:get("<->", 3)
260assert(x == "x" and y == "y" and z == "z") 260assert(b == 3 and x == "x" and y == "y" and z == "z")
261local x,y,z,w = linda:get("<->", 4) 261local b,x,y,z,w = linda:get("<->", 4)
262assert(x == "x" and y == "y" and z == "z" and w == nil) 262assert(b == 3 and x == "x" and y == "y" and z == "z" and w == nil)
263local k, x = linda:receive("<->") 263local k, x = linda:receive("<->")
264assert(k == "<->" and x == "x") 264assert(k == "<->" and x == "x")
265local k,y,z = linda:receive(linda.batched, "<->", 2) 265local k,y,z = linda:receive(linda.batched, "<->", 2)
266assert(k == "<->" and y == "y" and z == "z") 266assert(k == "<->" and y == "y" and z == "z")
267linda:set("<->") 267linda:set("<->")
268local x,y,z,w = linda:get("<->", 4) 268local b,x,y,z,w = linda:get("<->", 4)
269assert(x == nil and y == nil and z == nil and w == nil) 269assert(b == 0 and x == nil and y == nil and z == nil and w == nil)
270WR "ok\n" 270WR "ok\n"
271 271
272local function PEEK(...) return linda:get("<-", ...) end 272local function PEEK(...) return linda:get("<-", ...) end
@@ -282,7 +282,7 @@ SEND(setmetatable({"should be ignored"},{__lanesconvert=lanes.null})); WR("main
282for i=1,40 do 282for 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
286end 286end
287SEND(nil); WR("\nmain ", "nil sent\n") 287SEND(nil); WR("\nmain ", "nil sent\n")
288 288
@@ -306,7 +306,7 @@ assert(null==nil)
306local out_t = RECEIVE(); WR(type(out_t).." received\n") 306local out_t = RECEIVE(); WR(type(out_t).." received\n")
307assert(tables_match(out_t, {'a','b','c',d=10})) 307assert(tables_match(out_t, {'a','b','c',d=10}))
308 308
309assert(PEEK() == nil) 309assert(PEEK() == 0)
310SEND(4) 310SEND(4)
311 311
312local complex_table = RECEIVE(); WR(type(complex_table).." received\n") 312local 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"
16end 17end
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"
24end 26end
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"
75end 79end
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)
18end 18end
19 19
20-- ################################################################################################# 20-- #################################################################################################
21if false then 21if 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 )
94linda:receive( 0, T1 ) -- clear out; there could be one tick left 94linda:receive( 0, T1 ) -- clear out; there could be one tick left
95linda:receive( 0, T2 ) 95linda:receive( 0, T2 )
96 96
97assert( linda:get(T1) == nil ) 97local _count, _val = linda:get(T1)
98assert( linda:get(T2) == nil ) 98assert(_count == 0 and _val == nil)
99
100local _count, _val = linda:get(T2)
101assert(_count == 0 and _val == nil)
99 102
100PRINT "...making sure no ticks are coming..." 103PRINT "...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)
75end 76end
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)
120end 123end
121 124
122WR "================================================================================================" 125WR "================================================================================================"