aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-06-04 16:19:47 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-06-04 16:19:47 +0200
commitc360af2df3e0904c7338cc1f32fcb270b7e34092 (patch)
tree22b27902eff9148e787c968f5bd60ad44252c3a3
parent4695cbe96514e6b174fb5050bb2e9a41514091f3 (diff)
downloadlanes-c360af2df3e0904c7338cc1f32fcb270b7e34092.tar.gz
lanes-c360af2df3e0904c7338cc1f32fcb270b7e34092.tar.bz2
lanes-c360af2df3e0904c7338cc1f32fcb270b7e34092.zip
Refactored keeper implementation of linda:receive()
-rw-r--r--docs/index.html3
-rw-r--r--src/keeper.cpp94
-rw-r--r--src/linda.cpp5
-rw-r--r--tests/basic.lua2
-rw-r--r--tests/errhangtest.lua9
-rw-r--r--tests/linda_perf.lua1
6 files changed, 61 insertions, 53 deletions
diff --git a/docs/index.html b/docs/index.html
index 0cad4a0..1b24422 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -1224,7 +1224,8 @@
1224 The <tt>send()</tt> and <tt>receive()</tt> methods use Linda keys as FIFO stacks (first in, first out).<br/> 1224 The <tt>send()</tt> and <tt>receive()</tt> methods use Linda keys as FIFO stacks (first in, first out).<br/>
1225 By default, stack 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/> 1225 By default, stack 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/>
1226 A limit of 0 is allowed to block everything.<br/> 1226 A limit of 0 is allowed to block everything.<br/>
1227 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. 1227 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/>
1228 In batched mode, <tt>linda:receive()</tt> will raise an error if <tt>min_count < 1</tt> or <tt>max_count < min_count</tt>.
1228</p> 1229</p>
1229 1230
1230<p> 1231<p>
diff --git a/src/keeper.cpp b/src/keeper.cpp
index a546868..b73533a 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -81,7 +81,7 @@ class KeyUD
81 [[nodiscard]] static KeyUD* Create(KeeperState K_); 81 [[nodiscard]] static KeyUD* Create(KeeperState K_);
82 [[nodiscard]] static KeyUD* GetPtr(KeeperState K_, int idx_); 82 [[nodiscard]] static KeyUD* GetPtr(KeeperState K_, int idx_);
83 void peek(KeeperState K_, int count_); 83 void peek(KeeperState K_, int count_);
84 void pop(KeeperState K_, int count_); 84 [[nodiscard]] int pop(KeeperState K_, int minCount_, int maxCount_);
85 void prepareAccess(KeeperState K_); 85 void prepareAccess(KeeperState K_);
86 void push(KeeperState K_, int count_); 86 void push(KeeperState K_, int count_);
87 [[nodiscard]] bool reset(KeeperState K_); 87 [[nodiscard]] bool reset(KeeperState K_);
@@ -150,14 +150,21 @@ void KeyUD::peek(KeeperState const K_, int const count_)
150 150
151// in: fifo 151// in: fifo
152// out: remove the fifo table from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) 152// out: remove the fifo table from the stack, push as many items as required on the stack (function assumes they exist in sufficient number)
153void KeyUD::pop(KeeperState const K_, int const count_) 153int KeyUD::pop(KeeperState const K_, int const minCount_, int const maxCount_)
154{ 154{
155 LUA_ASSERT(K_, lua_istable(K_, -1)); 155 if (count < minCount_) {
156 int const _fifo_idx{ lua_gettop(K_) }; // K_: ... fifo 156 // pop ourselves, return nothing
157 lua_pop(K_, 1); // K_: ... this
158 return 0;
159 }
160 int const _popCount{ std::min(count, maxCount_) };
161 LUA_ASSERT(K_, KeyUD::GetPtr(K_, -1) == this); // K_: ... this
162 prepareAccess(K_); // K_: ... fifo
163 int const _fifo_idx{ lua_gettop(K_) };
157 // each iteration pushes a value on the stack! 164 // each iteration pushes a value on the stack!
158 STACK_GROW(K_, count_ + 2); 165 STACK_GROW(K_, _popCount + 2);
159 // skip first item, we will push it last 166 // skip first item, we will push it last
160 for (int const _i : std::ranges::iota_view{ 1, count_ }) { 167 for (int const _i : std::ranges::iota_view{ 1, _popCount }) {
161 int const _at{ first + _i }; 168 int const _at{ first + _i };
162 // push item on the stack 169 // push item on the stack
163 lua_rawgeti(K_, _fifo_idx, _at); // K_: ... fifo val 170 lua_rawgeti(K_, _fifo_idx, _at); // K_: ... fifo val
@@ -166,20 +173,16 @@ void KeyUD::pop(KeeperState const K_, int const count_)
166 lua_rawseti(K_, _fifo_idx, _at); // K_: ... fifo val 173 lua_rawseti(K_, _fifo_idx, _at); // K_: ... fifo val
167 } 174 }
168 // now process first item 175 // now process first item
169 { 176 lua_rawgeti(K_, _fifo_idx, first); // K_: ... fifo vals val
170 int const _at{ first }; 177 lua_pushnil(K_); // K_: ... fifo vals val nil
171 lua_rawgeti(K_, _fifo_idx, _at); // K_: ... fifo vals val 178 lua_rawseti(K_, _fifo_idx, first); // K_: ... fifo vals val
172 lua_pushnil(K_); // K_: ... fifo vals val nil 179 lua_replace(K_, _fifo_idx); // K_: ... vals
173 lua_rawseti(K_, _fifo_idx, _at); // K_: ... fifo vals val
174 lua_replace(K_, _fifo_idx); // K_: ... vals
175 }
176 180
177 // avoid ever-growing indexes by resetting each time we detect the fifo is empty 181 // avoid ever-growing indexes by resetting each time we detect the fifo is empty
178 { 182 int const _new_count{ count - _popCount };
179 int const _new_count{ count - count_ }; 183 first = (_new_count == 0) ? 1 : (first + _popCount);
180 first = (_new_count == 0) ? 1 : (first + count_); 184 count = _new_count;
181 count = _new_count; 185 return _popCount;
182 }
183} 186}
184 187
185// ################################################################################################# 188// #################################################################################################
@@ -466,21 +469,20 @@ int keepercall_receive(lua_State* const L_)
466 PushKeysDB(_K, 1); // _K: linda keys... KeysDB 469 PushKeysDB(_K, 1); // _K: linda keys... KeysDB
467 lua_replace(_K, 1); // _K: KeysDB keys... 470 lua_replace(_K, 1); // _K: KeysDB keys...
468 471
469 for (int const _i : std::ranges::iota_view{ 2, _top + 1 }) { 472 for (int const _keyIdx : std::ranges::iota_view{ 2, _top + 1 }) {
470 lua_pushvalue(_K, _i); // _K: KeysDB keys... key[i] 473 lua_pushvalue(_K, _keyIdx); // _K: KeysDB keys... key[i]
471 lua_rawget(_K, 1); // _K: KeysDB keys... KeyUD 474 lua_rawget(_K, 1); // _K: KeysDB keys... KeyUD
472 KeyUD* const _key{ KeyUD::GetPtr(_K, -1) }; 475 KeyUD* const _key{ KeyUD::GetPtr(_K, -1) };
473 if (_key != nullptr && _key->count > 0) { 476 if (_key != nullptr) { // it's fine to attempt a read on a key that wasn't yet written to
474 _key->prepareAccess(_K); // _K: KeysDB keys... fifo 477 int const _popped{ _key->pop(_K, 1, 1) }; // _K: KeysDB keys... val
475 _key->pop(_K, 1); // _K: KeysDB keys... val 478 if (_popped > 0) {
476 if (!lua_isnil(_K, -1)) {
477 lua_replace(_K, 1); // _K: val keys... 479 lua_replace(_K, 1); // _K: val keys...
478 lua_settop(_K, _i); // _K: val keys... key[i] 480 lua_settop(_K, _keyIdx); // _K: val keys... key[i]
479 if (_i != 2) { 481 if (_keyIdx != 2) {
480 lua_replace(_K, 2); // _K: val key keys... 482 lua_replace(_K, 2); // _K: val key[i] keys...
481 lua_settop(_K, 2); // _K: val key 483 lua_settop(_K, 2); // _K: val key[i]
482 } 484 }
483 lua_insert(_K, 1); // _K: key, val 485 lua_insert(_K, 1); // _K: key val
484 return 2; 486 return 2;
485 } 487 }
486 } 488 }
@@ -496,27 +498,23 @@ int keepercall_receive(lua_State* const L_)
496int keepercall_receive_batched(lua_State* const L_) 498int keepercall_receive_batched(lua_State* const L_)
497{ 499{
498 KeeperState const _K{ L_ }; 500 KeeperState const _K{ L_ };
501 // linda:receive() made sure that _min_count > 0 and _max_count > _min_count
499 int const _min_count{ static_cast<int>(lua_tointeger(_K, 3)) }; 502 int const _min_count{ static_cast<int>(lua_tointeger(_K, 3)) };
500 if (_min_count > 0) { 503 int const _max_count{ static_cast<int>(luaL_optinteger(_K, 4, _min_count)) };
501 int const _max_count{ static_cast<int>(luaL_optinteger(_K, 4, _min_count)) }; 504 lua_settop(_K, 2); // _K: linda key
502 lua_settop(_K, 2); // _K: linda key 505 lua_insert(_K, 1); // _K: key linda
503 lua_insert(_K, 1); // _K: key linda 506 PushKeysDB(_K, 2); // _K: key linda KeysDB
504 PushKeysDB(_K, 2); // _K: key linda KeysDB 507 lua_remove(_K, 2); // _K: key KeysDB
505 lua_remove(_K, 2); // _K: key KeysDB 508 lua_pushvalue(_K, 1); // _K: key KeysDB key
506 lua_pushvalue(_K, 1); // _K: key KeysDB key 509 lua_rawget(_K, 2); // _K: key KeysDB KeyUD
507 lua_rawget(_K, 2); // _K: key KeysDB KeyUD 510 lua_remove(_K, 2); // _K: key KeyUD
508 lua_remove(_K, 2); // _K: key KeyUD 511 KeyUD* const _key{ KeyUD::GetPtr(_K, -1) };
509 KeyUD* const _key{ KeyUD::GetPtr(_K, -1) }; 512 if (_key == nullptr || _key->pop(_K, _min_count, _max_count) == 0) { // _K: [key val...]|crap
510 if (_key != nullptr && _key->count >= _min_count) { 513 // Lua will adjust the stack for us when we return
511 _key->prepareAccess(_K); // _K: key fifo
512 _key->pop(_K, std::min(_max_count, _key->count)); // _K: key val...
513 } else {
514 lua_settop(_K, 0); // _K:
515 }
516 // return whatever remains on the stack at that point
517 return lua_gettop(_K);
518 } else {
519 return 0; 514 return 0;
515 } else {
516 // return whatever remains on the stack at that point: the key and the values we pulled from the fifo
517 return lua_gettop(_K);
520 } 518 }
521} 519}
522 520
diff --git a/src/linda.cpp b/src/linda.cpp
index 12b5e8a..b623d29 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -470,12 +470,15 @@ LUAG_FUNC(linda_receive)
470 _selected_keeper_receive = KEEPER_API(receive_batched); 470 _selected_keeper_receive = KEEPER_API(receive_batched);
471 // we expect a user-defined amount of return value 471 // we expect a user-defined amount of return value
472 _expected_pushed_min = (int) luaL_checkinteger(L_, _key_i + 1); 472 _expected_pushed_min = (int) luaL_checkinteger(L_, _key_i + 1);
473 if (_expected_pushed_min < 1) {
474 raise_luaL_argerror(L_, _key_i + 1, "bad min count");
475 }
473 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min); 476 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min);
474 // don't forget to count the key in addition to the values 477 // don't forget to count the key in addition to the values
475 ++_expected_pushed_min; 478 ++_expected_pushed_min;
476 ++_expected_pushed_max; 479 ++_expected_pushed_max;
477 if (_expected_pushed_min > _expected_pushed_max) { 480 if (_expected_pushed_min > _expected_pushed_max) {
478 raise_luaL_error(L_, "batched min/max error"); 481 raise_luaL_argerror(L_, _key_i + 2, "batched min/max error");
479 } 482 }
480 } else { 483 } else {
481 // make sure the keys are of a valid type 484 // make sure the keys are of a valid type
diff --git a/tests/basic.lua b/tests/basic.lua
index ecd93fc..8522895 100644
--- a/tests/basic.lua
+++ b/tests/basic.lua
@@ -433,7 +433,7 @@ local function chunk2(linda)
433 assert(config.strip_functions and info.short_src=="?" or string.match(info.short_src, "^.*basic.lua$"), "bad info.short_src") 433 assert(config.strip_functions and info.short_src=="?" or string.match(info.short_src, "^.*basic.lua$"), "bad info.short_src")
434 -- These vary so let's not be picky (they're there..) 434 -- These vary so let's not be picky (they're there..)
435 -- 435 --
436 assert(info.linedefined == 400, "bad linedefined") -- start of 'chunk2' 436 assert(info.linedefined == 419, "bad linedefined") -- start of 'chunk2'
437 assert(config.strip_functions and info.currentline==-1 or info.currentline > info.linedefined, "bad currentline") -- line of 'debug.getinfo' 437 assert(config.strip_functions and info.currentline==-1 or info.currentline > info.linedefined, "bad currentline") -- line of 'debug.getinfo'
438 assert(info.lastlinedefined > info.currentline, "bad lastlinedefined") -- end of 'chunk2' 438 assert(info.lastlinedefined > info.currentline, "bad lastlinedefined") -- end of 'chunk2'
439 local k,func= linda:receive("down") 439 local k,func= linda:receive("down")
diff --git a/tests/errhangtest.lua b/tests/errhangtest.lua
index 99f44b2..d0ffcc4 100644
--- a/tests/errhangtest.lua
+++ b/tests/errhangtest.lua
@@ -1,4 +1,8 @@
1local lanes = require "lanes".configure{with_timers=false} 1local lanes = require "lanes".configure{with_timers=false,strip_functions=false}
2
3local require_assert_result_1, require_assert_result_2 = require "assert" -- assert.fails()
4print("require_assert_result:", require_assert_result_1, require_assert_result_2)
5
2local linda = lanes.linda() 6local linda = lanes.linda()
3 7
4-- we are not allowed to send coroutines through a lane 8-- we are not allowed to send coroutines through a lane
@@ -22,6 +26,9 @@ end
22-- get/set a few values 26-- get/set a few values
23if true then 27if true then
24 print "\n#### set 3 -> receive batched" 28 print "\n#### set 3 -> receive batched"
29 assert.fails(function() linda:receive(linda.batched, "some key", -1, 1) end)
30 assert.fails(function() linda:receive(linda.batched, "some key", 2, 1) end)
31 assert.failsnot(function() linda:receive(0, linda.batched, "some key", 1, 3) end)
25 local fun = function() print "function test ok" end 32 local fun = function() print "function test ok" end
26 print(pcall(linda.set, linda, 'test', true, nil, fun)) 33 print(pcall(linda.set, linda, 'test', true, nil, fun))
27 -- read back the contents 34 -- read back the contents
diff --git a/tests/linda_perf.lua b/tests/linda_perf.lua
index 4d35380..b173d12 100644
--- a/tests/linda_perf.lua
+++ b/tests/linda_perf.lua
@@ -124,7 +124,6 @@ do
124 end 124 end
125end 125end
126 126
127do return end
128-- ################################################################################################# 127-- #################################################################################################
129 128
130TEST1 = TEST1 or 1000 129TEST1 = TEST1 or 1000