diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-04 16:19:47 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-06-04 16:19:47 +0200 |
commit | c360af2df3e0904c7338cc1f32fcb270b7e34092 (patch) | |
tree | 22b27902eff9148e787c968f5bd60ad44252c3a3 | |
parent | 4695cbe96514e6b174fb5050bb2e9a41514091f3 (diff) | |
download | lanes-c360af2df3e0904c7338cc1f32fcb270b7e34092.tar.gz lanes-c360af2df3e0904c7338cc1f32fcb270b7e34092.tar.bz2 lanes-c360af2df3e0904c7338cc1f32fcb270b7e34092.zip |
Refactored keeper implementation of linda:receive()
-rw-r--r-- | docs/index.html | 3 | ||||
-rw-r--r-- | src/keeper.cpp | 94 | ||||
-rw-r--r-- | src/linda.cpp | 5 | ||||
-rw-r--r-- | tests/basic.lua | 2 | ||||
-rw-r--r-- | tests/errhangtest.lua | 9 | ||||
-rw-r--r-- | tests/linda_perf.lua | 1 |
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) |
153 | void KeyUD::pop(KeeperState const K_, int const count_) | 153 | int 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_) | |||
496 | int keepercall_receive_batched(lua_State* const L_) | 498 | int 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 @@ | |||
1 | local lanes = require "lanes".configure{with_timers=false} | 1 | local lanes = require "lanes".configure{with_timers=false,strip_functions=false} |
2 | |||
3 | local require_assert_result_1, require_assert_result_2 = require "assert" -- assert.fails() | ||
4 | print("require_assert_result:", require_assert_result_1, require_assert_result_2) | ||
5 | |||
2 | local linda = lanes.linda() | 6 | local 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 |
23 | if true then | 27 | if 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 |
125 | end | 125 | end |
126 | 126 | ||
127 | do return end | ||
128 | -- ################################################################################################# | 127 | -- ################################################################################################# |
129 | 128 | ||
130 | TEST1 = TEST1 or 1000 | 129 | TEST1 = TEST1 or 1000 |