diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-31 15:13:48 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-31 15:13:48 +0200 |
| commit | 95e5bf6461be6e227466911d5e3a683d149df725 (patch) | |
| tree | db6bc91fda6d9059f3bcaaa37f4d56398d126d42 | |
| parent | a65a84cfad4eae38e5e84b1ab11f60a62833f287 (diff) | |
| download | lanes-95e5bf6461be6e227466911d5e3a683d149df725.tar.gz lanes-95e5bf6461be6e227466911d5e3a683d149df725.tar.bz2 lanes-95e5bf6461be6e227466911d5e3a683d149df725.zip | |
Start modernizing Keeper implementation
| -rw-r--r-- | src/keeper.cpp | 410 | ||||
| -rw-r--r-- | src/linda.cpp | 26 | ||||
| -rw-r--r-- | src/linda.h | 1 | ||||
| -rw-r--r-- | tests/keeper.lua | 6 |
4 files changed, 231 insertions, 212 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp index ea924e8..26e5723 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
| @@ -49,10 +49,13 @@ | |||
| 49 | #include <ranges> | 49 | #include <ranges> |
| 50 | 50 | ||
| 51 | // ################################################################################################# | 51 | // ################################################################################################# |
| 52 | // Keeper implementation | 52 | // ################################################################################################# |
| 53 | // ############################################ KeyUD ############################################## | ||
| 54 | // ################################################################################################# | ||
| 53 | // ################################################################################################# | 55 | // ################################################################################################# |
| 54 | 56 | ||
| 55 | class keeper_fifo | 57 | // the full userdata associated to a given Linda key to store its contents |
| 58 | class KeyUD | ||
| 56 | { | 59 | { |
| 57 | public: | 60 | public: |
| 58 | int first{ 1 }; | 61 | int first{ 1 }; |
| @@ -60,65 +63,59 @@ class keeper_fifo | |||
| 60 | int limit{ -1 }; | 63 | int limit{ -1 }; |
| 61 | 64 | ||
| 62 | // a fifo full userdata has one uservalue, the table that holds the actual fifo contents | 65 | // a fifo full userdata has one uservalue, the table that holds the actual fifo contents |
| 63 | [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return lua_newuserdatauv<keeper_fifo>(L_, 1); } | 66 | [[nodiscard]] static void* operator new([[maybe_unused]] size_t size_, KeeperState L_) noexcept { return lua_newuserdatauv<KeyUD>(L_, 1); } |
| 64 | // always embedded somewhere else or "in-place constructed" as a full userdata | 67 | // always embedded somewhere else or "in-place constructed" as a full userdata |
| 65 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception | 68 | // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception |
| 66 | static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); } | 69 | static void operator delete([[maybe_unused]] void* p_, [[maybe_unused]] KeeperState L_) { LUA_ASSERT(L_, !"should never be called"); } |
| 67 | 70 | ||
| 68 | [[nodiscard]] static keeper_fifo* getPtr(lua_State* L_, int idx_) | 71 | [[nodiscard]] static KeyUD* GetPtr(lua_State* L_, int idx_); |
| 69 | { | 72 | [[nodiscard]] static KeyUD* Create(KeeperState L_); |
| 70 | return lua_tofulluserdata<keeper_fifo>(L_, idx_); | 73 | [[nodiscard]] static KeyUD* PrepareAccess(lua_State* L_, int idx_); |
| 71 | } | 74 | void peek(lua_State* L_, int count_); |
| 75 | void pop(lua_State* L_, int count_); | ||
| 76 | void push(lua_State* L_, int count_); | ||
| 72 | }; | 77 | }; |
| 73 | 78 | ||
| 74 | static constexpr int kContentsTableIndex{ 1 }; | 79 | static constexpr int kContentsTableIndex{ 1 }; |
| 75 | 80 | ||
| 76 | // ################################################################################################# | 81 | // ################################################################################################# |
| 77 | 82 | ||
| 78 | // replaces the fifo ud by its uservalue on the stack | ||
| 79 | [[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L_, int idx_) | ||
| 80 | { | ||
| 81 | keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, idx_) }; | ||
| 82 | if (_fifo != nullptr) { | ||
| 83 | idx_ = lua_absindex(L_, idx_); | ||
| 84 | STACK_GROW(L_, 1); | ||
| 85 | // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around | ||
| 86 | lua_getiuservalue(L_, idx_, kContentsTableIndex); | ||
| 87 | lua_replace(L_, idx_); | ||
| 88 | } | ||
| 89 | return _fifo; | ||
| 90 | } | ||
| 91 | |||
| 92 | // ################################################################################################# | ||
| 93 | |||
| 94 | // in: nothing | 83 | // in: nothing |
| 95 | // out: { first = 1, count = 0, limit = -1} | 84 | // out: { first = 1, count = 0, limit = -1} |
| 96 | [[nodiscard]] static keeper_fifo* fifo_new(KeeperState L_) | 85 | KeyUD* KeyUD::Create(KeeperState const L_) |
| 97 | { | 86 | { |
| 98 | STACK_GROW(L_, 2); | 87 | STACK_GROW(L_, 2); |
| 99 | STACK_CHECK_START_REL(L_, 0); | 88 | STACK_CHECK_START_REL(L_, 0); |
| 100 | keeper_fifo* const _fifo{ new (L_) keeper_fifo{} }; | 89 | KeyUD* const _key{ new (L_) KeyUD{} }; |
| 101 | STACK_CHECK(L_, 1); | 90 | STACK_CHECK(L_, 1); |
| 102 | lua_newtable(L_); | 91 | lua_newtable(L_); |
| 103 | lua_setiuservalue(L_, -2, kContentsTableIndex); | 92 | lua_setiuservalue(L_, -2, kContentsTableIndex); |
| 104 | STACK_CHECK(L_, 1); | 93 | STACK_CHECK(L_, 1); |
| 105 | return _fifo; | 94 | return _key; |
| 106 | } | 95 | } |
| 107 | 96 | ||
| 108 | // ################################################################################################# | 97 | // ################################################################################################# |
| 109 | 98 | ||
| 110 | // in: expect fifo ... on top of the stack | 99 | KeyUD* KeyUD::GetPtr(lua_State* L_, int idx_) |
| 111 | // out: nothing, removes all pushed values from the stack | ||
| 112 | static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_) | ||
| 113 | { | 100 | { |
| 114 | int const _idx{ lua_gettop(L_) - count_ }; | 101 | return lua_tofulluserdata<KeyUD>(L_, idx_); |
| 115 | int const _start{ fifo_->first + fifo_->count - 1 }; | 102 | } |
| 116 | // pop all additional arguments, storing them in the fifo | 103 | |
| 117 | for (int const _i : std::ranges::reverse_view{ std::ranges::iota_view{ 1, count_ + 1 } }) { | 104 | |
| 118 | // store in the fifo the value at the top of the stack at the specified index, popping it from the stack | 105 | // ################################################################################################# |
| 119 | lua_rawseti(L_, _idx, _start + _i); | 106 | |
| 107 | // replaces the fifo ud by its uservalue on the stack | ||
| 108 | KeyUD* KeyUD::PrepareAccess(lua_State* const L_, int const idx_) | ||
| 109 | { | ||
| 110 | KeyUD* const _key{ KeyUD::GetPtr(L_, idx_) }; | ||
| 111 | if (_key) { | ||
| 112 | int const _idx{ lua_absindex(L_, idx_) }; | ||
| 113 | STACK_GROW(L_, 1); | ||
| 114 | // we can replace the key userdata in the stack without fear of it being GCed, there are other references around | ||
| 115 | lua_getiuservalue(L_, _idx, kContentsTableIndex); | ||
| 116 | lua_replace(L_, _idx); | ||
| 120 | } | 117 | } |
| 121 | fifo_->count += count_; | 118 | return _key; |
| 122 | } | 119 | } |
| 123 | 120 | ||
| 124 | // ################################################################################################# | 121 | // ################################################################################################# |
| @@ -128,11 +125,11 @@ static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
| 128 | // expects exactly 1 value on the stack! | 125 | // expects exactly 1 value on the stack! |
| 129 | // currently only called with a count of 1, but this may change in the future | 126 | // currently only called with a count of 1, but this may change in the future |
| 130 | // function assumes that there is enough data in the fifo to satisfy the request | 127 | // function assumes that there is enough data in the fifo to satisfy the request |
| 131 | static void fifo_peek(lua_State* const L_, keeper_fifo const* const fifo_, int const count_) | 128 | void KeyUD::peek(lua_State* const L_, int const count_) |
| 132 | { | 129 | { |
| 133 | STACK_GROW(L_, count_); | 130 | STACK_GROW(L_, count_); |
| 134 | for (int const _i : std::ranges::iota_view{ 0, count_ }) { | 131 | for (int const _i : std::ranges::iota_view{ 0, count_ }) { |
| 135 | lua_rawgeti(L_, 1, (fifo_->first + _i)); | 132 | lua_rawgeti(L_, 1, first + _i); |
| 136 | } | 133 | } |
| 137 | } | 134 | } |
| 138 | 135 | ||
| @@ -140,7 +137,7 @@ static void fifo_peek(lua_State* const L_, keeper_fifo const* const fifo_, int c | |||
| 140 | 137 | ||
| 141 | // in: fifo | 138 | // in: fifo |
| 142 | // out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) | 139 | // out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) |
| 143 | static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_) | 140 | void KeyUD::pop(lua_State* const L_, int const count_) |
| 144 | { | 141 | { |
| 145 | LUA_ASSERT(L_, lua_istable(L_, -1)); | 142 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
| 146 | int const _fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl | 143 | int const _fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl |
| @@ -148,7 +145,7 @@ static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
| 148 | STACK_GROW(L_, count_ + 2); | 145 | STACK_GROW(L_, count_ + 2); |
| 149 | // skip first item, we will push it last | 146 | // skip first item, we will push it last |
| 150 | for (int const _i : std::ranges::iota_view{ 1, count_ }) { | 147 | for (int const _i : std::ranges::iota_view{ 1, count_ }) { |
| 151 | int const _at{ fifo_->first + _i }; | 148 | int const _at{ first + _i }; |
| 152 | // push item on the stack | 149 | // push item on the stack |
| 153 | lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl val | 150 | lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl val |
| 154 | // remove item from the fifo | 151 | // remove item from the fifo |
| @@ -157,7 +154,7 @@ static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
| 157 | } | 154 | } |
| 158 | // now process first item | 155 | // now process first item |
| 159 | { | 156 | { |
| 160 | int const _at{ fifo_->first }; | 157 | int const _at{ first }; |
| 161 | lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val | 158 | lua_rawgeti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val |
| 162 | lua_pushnil(L_); // L_: ... fifotbl vals val nil | 159 | lua_pushnil(L_); // L_: ... fifotbl vals val nil |
| 163 | lua_rawseti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val | 160 | lua_rawseti(L_, _fifo_idx, _at); // L_: ... fifotbl vals val |
| @@ -166,14 +163,31 @@ static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
| 166 | 163 | ||
| 167 | // avoid ever-growing indexes by resetting each time we detect the fifo is empty | 164 | // avoid ever-growing indexes by resetting each time we detect the fifo is empty |
| 168 | { | 165 | { |
| 169 | int const _new_count{ fifo_->count - count_ }; | 166 | int const _new_count{ count - count_ }; |
| 170 | fifo_->first = (_new_count == 0) ? 1 : (fifo_->first + count_); | 167 | first = (_new_count == 0) ? 1 : (first + count_); |
| 171 | fifo_->count = _new_count; | 168 | count = _new_count; |
| 172 | } | 169 | } |
| 173 | } | 170 | } |
| 174 | 171 | ||
| 175 | // ################################################################################################# | 172 | // ################################################################################################# |
| 176 | 173 | ||
| 174 | // in: expect fifo ... on top of the stack | ||
| 175 | // out: nothing, removes all pushed values from the stack | ||
| 176 | void KeyUD::push(lua_State* const L_, int const count_) | ||
| 177 | { | ||
| 178 | int const _idx{ lua_gettop(L_) - count_ }; | ||
| 179 | int const _start{ first + count - 1 }; | ||
| 180 | // pop all additional arguments, storing them in the fifo | ||
| 181 | for (int const _i : std::ranges::reverse_view{ std::ranges::iota_view{ 1, count_ + 1 } }) { | ||
| 182 | // store in the fifo the value at the top of the stack at the specified index, popping it from the stack | ||
| 183 | lua_rawseti(L_, _idx, _start + _i); | ||
| 184 | } | ||
| 185 | count += count_; | ||
| 186 | } | ||
| 187 | |||
| 188 | // ################################################################################################# | ||
| 189 | // ################################################################################################# | ||
| 190 | |||
| 177 | // in: linda_ud expected at stack slot idx | 191 | // in: linda_ud expected at stack slot idx |
| 178 | // out: fifos[ud] | 192 | // out: fifos[ud] |
| 179 | // xxh64 of string "kFifosRegKey" generated at https://www.pelock.com/products/hash-calculator | 193 | // xxh64 of string "kFifosRegKey" generated at https://www.pelock.com/products/hash-calculator |
| @@ -226,19 +240,19 @@ int keeper_push_linda_storage(Linda& linda_, DestState L_) | |||
| 226 | InterCopyContext _c{ linda_.U, L_, _KL, {}, {}, {}, LookupMode::FromKeeper, {} }; | 240 | InterCopyContext _c{ linda_.U, L_, _KL, {}, {}, {}, LookupMode::FromKeeper, {} }; |
| 227 | lua_pushnil(_KL); // KL: storage nil L_: out | 241 | lua_pushnil(_KL); // KL: storage nil L_: out |
| 228 | while (lua_next(_KL, -2)) { // KL: storage key fifo L_: out | 242 | while (lua_next(_KL, -2)) { // KL: storage key fifo L_: out |
| 229 | keeper_fifo* fifo = prepare_fifo_access(_KL, -1); // KL: storage key fifotbl L_: out | 243 | KeyUD* const _keyUD{ KeyUD::PrepareAccess(_KL, -1) }; // KL: storage key fifotbl L_: out |
| 230 | lua_pushvalue(_KL, -2); // KL: storage key fifotbl key L_: out | 244 | lua_pushvalue(_KL, -2); // KL: storage key fifotbl key L_: out |
| 231 | std::ignore = _c.inter_move(1); // KL: storage key fifotbl L_: out key | 245 | std::ignore = _c.inter_move(1); // KL: storage key fifotbl L_: out key |
| 232 | STACK_CHECK(L_, 2); | 246 | STACK_CHECK(L_, 2); |
| 233 | lua_newtable(L_); // KL: storage key L_: out key keyout | 247 | lua_newtable(L_); // KL: storage key L_: out key keyout |
| 234 | std::ignore = _c.inter_move(1); // KL: storage key L_: out key keyout fifotbl | 248 | std::ignore = _c.inter_move(1); // KL: storage key L_: out key keyout fifotbl |
| 235 | lua_pushinteger(L_, fifo->first); // KL: storage key L_: out key keyout fifotbl first | 249 | lua_pushinteger(L_, _keyUD->first); // KL: storage key L_: out key keyout fifotbl first |
| 236 | STACK_CHECK(L_, 5); | 250 | STACK_CHECK(L_, 5); |
| 237 | lua_setfield(L_, -3, "first"); // KL: storage key L_: out key keyout fifotbl | 251 | lua_setfield(L_, -3, "first"); // KL: storage key L_: out key keyout fifotbl |
| 238 | lua_pushinteger(L_, fifo->count); // KL: storage key L_: out key keyout fifobtl count | 252 | lua_pushinteger(L_, _keyUD->count); // KL: storage key L_: out key keyout fifobtl count |
| 239 | STACK_CHECK(L_, 5); | 253 | STACK_CHECK(L_, 5); |
| 240 | lua_setfield(L_, -3, "count"); // KL: storage key L_: out key keyout fifotbl | 254 | lua_setfield(L_, -3, "count"); // KL: storage key L_: out key keyout fifotbl |
| 241 | lua_pushinteger(L_, fifo->limit); // KL: storage key L_: out key keyout fifotbl limit | 255 | lua_pushinteger(L_, _keyUD->limit); // KL: storage key L_: out key keyout fifotbl limit |
| 242 | STACK_CHECK(L_, 5); | 256 | STACK_CHECK(L_, 5); |
| 243 | lua_setfield(L_, -3, "limit"); // KL: storage key L_: out key keyout fifotbl | 257 | lua_setfield(L_, -3, "limit"); // KL: storage key L_: out key keyout fifotbl |
| 244 | lua_setfield(L_, -2, "fifo"); // KL: storage key L_: out key keyout | 258 | lua_setfield(L_, -2, "fifo"); // KL: storage key L_: out key keyout |
| @@ -252,9 +266,13 @@ int keeper_push_linda_storage(Linda& linda_, DestState L_) | |||
| 252 | } | 266 | } |
| 253 | 267 | ||
| 254 | // ################################################################################################# | 268 | // ################################################################################################# |
| 269 | // ################################################################################################# | ||
| 270 | // ######################################## keepercall_XXX ######################################### | ||
| 271 | // ################################################################################################# | ||
| 272 | // ################################################################################################# | ||
| 255 | 273 | ||
| 256 | // in: linda_ud | 274 | // in: linda_ud |
| 257 | int keepercall_clear(lua_State* L_) | 275 | int keepercall_clear(lua_State* const L_) |
| 258 | { | 276 | { |
| 259 | STACK_GROW(L_, 3); | 277 | STACK_GROW(L_, 3); |
| 260 | STACK_CHECK_START_REL(L_, 0); | 278 | STACK_CHECK_START_REL(L_, 0); |
| @@ -271,7 +289,7 @@ int keepercall_clear(lua_State* L_) | |||
| 271 | 289 | ||
| 272 | // in: linda_ud, key, ... | 290 | // in: linda_ud, key, ... |
| 273 | // out: true|false | 291 | // out: true|false |
| 274 | int keepercall_send(lua_State* L_) | 292 | int keepercall_send(lua_State* const L_) |
| 275 | { | 293 | { |
| 276 | int const _n{ lua_gettop(L_) - 2 }; | 294 | int const _n{ lua_gettop(L_) - 2 }; |
| 277 | push_table(L_, 1); // L_: ud key ... fifos | 295 | push_table(L_, 1); // L_: ud key ... fifos |
| @@ -280,20 +298,20 @@ int keepercall_send(lua_State* L_) | |||
| 280 | lua_rawget(L_, -2); // L_: ud key ... fifos fifo | 298 | lua_rawget(L_, -2); // L_: ud key ... fifos fifo |
| 281 | if (lua_isnil(L_, -1)) { | 299 | if (lua_isnil(L_, -1)) { |
| 282 | lua_pop(L_, 1); // L_: ud key ... fifos | 300 | lua_pop(L_, 1); // L_: ud key ... fifos |
| 283 | std::ignore = fifo_new(KeeperState{ L_ }); // L_: ud key ... fifos fifo | 301 | std::ignore = KeyUD::Create(KeeperState{ L_ }); // L_: ud key ... fifos fifo |
| 284 | lua_pushvalue(L_, 2); // L_: ud key ... fifos fifo key | 302 | lua_pushvalue(L_, 2); // L_: ud key ... fifos fifo key |
| 285 | lua_pushvalue(L_, -2); // L_: ud key ... fifos fifo key fifo | 303 | lua_pushvalue(L_, -2); // L_: ud key ... fifos fifo key fifo |
| 286 | lua_rawset(L_, -4); // L_: ud key ... fifos fifo | 304 | lua_rawset(L_, -4); // L_: ud key ... fifos fifo |
| 287 | } | 305 | } |
| 288 | lua_remove(L_, -2); // L_: ud key ... fifo | 306 | lua_remove(L_, -2); // L_: ud key ... fifo |
| 289 | keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) }; | 307 | KeyUD* _key{ KeyUD::GetPtr(L_, -1) }; |
| 290 | if (_fifo->limit >= 0 && _fifo->count + _n > _fifo->limit) { | 308 | if (_key->limit >= 0 && _key->count + _n > _key->limit) { |
| 291 | lua_settop(L_, 0); // L_: | 309 | lua_settop(L_, 0); // L_: |
| 292 | lua_pushboolean(L_, 0); // L_:false | 310 | lua_pushboolean(L_, 0); // L_:false |
| 293 | } else { | 311 | } else { |
| 294 | _fifo = prepare_fifo_access(L_, -1); // L_: ud fifotbl | 312 | _key = KeyUD::PrepareAccess(L_, -1); // L_: ud fifotbl |
| 295 | lua_replace(L_, 2); // L_: ud fifotbl ... | 313 | lua_replace(L_, 2); // L_: ud fifotbl ... |
| 296 | fifo_push(L_, _fifo, _n); // L_: ud fifotbl | 314 | _key->push(L_, _n); // L_: ud fifotbl |
| 297 | lua_settop(L_, 0); // L_: | 315 | lua_settop(L_, 0); // L_: |
| 298 | lua_pushboolean(L_, 1); // L_: true | 316 | lua_pushboolean(L_, 1); // L_: true |
| 299 | } | 317 | } |
| @@ -304,17 +322,18 @@ int keepercall_send(lua_State* L_) | |||
| 304 | 322 | ||
| 305 | // in: linda_ud, key [, key]? | 323 | // in: linda_ud, key [, key]? |
| 306 | // out: (key, val) or nothing | 324 | // out: (key, val) or nothing |
| 307 | int keepercall_receive(lua_State* L_) | 325 | int keepercall_receive(lua_State* const L_) |
| 308 | { | 326 | { |
| 309 | int const _top{ lua_gettop(L_) }; | 327 | int const _top{ lua_gettop(L_) }; |
| 310 | push_table(L_, 1); // L_: ud keys fifos | 328 | push_table(L_, 1); // L_: ud keys fifos |
| 311 | lua_replace(L_, 1); // L_: fifos keys | 329 | lua_replace(L_, 1); // L_: fifos keys |
| 312 | for (int _i = 2; _i <= _top; ++_i) { | 330 | |
| 331 | for (int const _i : std::ranges::iota_view{ 2, _top + 1 }) { | ||
| 313 | lua_pushvalue(L_, _i); // L_: fifos keys key[i] | 332 | lua_pushvalue(L_, _i); // L_: fifos keys key[i] |
| 314 | lua_rawget(L_, 1); // L_: fifos keys fifo | 333 | lua_rawget(L_, 1); // L_: fifos keys fifo |
| 315 | keeper_fifo* const _fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos keys fifotbl | 334 | KeyUD* const _key{ KeyUD::PrepareAccess(L_, -1) }; // L_: fifos keys fifotbl |
| 316 | if (_fifo != nullptr && _fifo->count > 0) { | 335 | if (_key != nullptr && _key->count > 0) { |
| 317 | fifo_pop(L_, _fifo, 1); // L_: fifos keys val | 336 | _key->pop(L_, 1); // L_: fifos keys val |
| 318 | if (!lua_isnil(L_, -1)) { | 337 | if (!lua_isnil(L_, -1)) { |
| 319 | lua_replace(L_, 1); // L_: val keys | 338 | lua_replace(L_, 1); // L_: val keys |
| 320 | lua_settop(L_, _i); // L_: val keys key[i] | 339 | lua_settop(L_, _i); // L_: val keys key[i] |
| @@ -335,7 +354,7 @@ int keepercall_receive(lua_State* L_) | |||
| 335 | // ################################################################################################# | 354 | // ################################################################################################# |
| 336 | 355 | ||
| 337 | // in: linda_ud key mincount [maxcount] | 356 | // in: linda_ud key mincount [maxcount] |
| 338 | int keepercall_receive_batched(lua_State* L_) | 357 | int keepercall_receive_batched(lua_State* const L_) |
| 339 | { | 358 | { |
| 340 | int const _min_count{ static_cast<int>(lua_tointeger(L_, 3)) }; | 359 | int const _min_count{ static_cast<int>(lua_tointeger(L_, 3)) }; |
| 341 | if (_min_count > 0) { | 360 | if (_min_count > 0) { |
| @@ -347,9 +366,9 @@ int keepercall_receive_batched(lua_State* L_) | |||
| 347 | lua_pushvalue(L_, 1); // L_: key fifos key | 366 | lua_pushvalue(L_, 1); // L_: key fifos key |
| 348 | lua_rawget(L_, 2); // L_: key fifos fifo | 367 | lua_rawget(L_, 2); // L_: key fifos fifo |
| 349 | lua_remove(L_, 2); // L_: key fifo | 368 | lua_remove(L_, 2); // L_: key fifo |
| 350 | keeper_fifo* const _fifo{ prepare_fifo_access(L_, 2) }; // L_: key fifotbl | 369 | KeyUD* const _key{ KeyUD::PrepareAccess(L_, 2) }; // L_: key fifotbl |
| 351 | if (_fifo != nullptr && _fifo->count >= _min_count) { | 370 | if (_key != nullptr && _key->count >= _min_count) { |
| 352 | fifo_pop(L_, _fifo, std::min(_max_count, _fifo->count)); // L_: key ... | 371 | _key->pop(L_, std::min(_max_count, _key->count)); // L_: key ... |
| 353 | } else { | 372 | } else { |
| 354 | lua_settop(L_, 0); // L_: | 373 | lua_settop(L_, 0); // L_: |
| 355 | } | 374 | } |
| @@ -363,7 +382,7 @@ int keepercall_receive_batched(lua_State* L_) | |||
| 363 | 382 | ||
| 364 | // in: linda_ud key [n|nil] | 383 | // in: linda_ud key [n|nil] |
| 365 | // out: true or nil | 384 | // out: true or nil |
| 366 | int keepercall_limit(lua_State* L_) | 385 | int keepercall_limit(lua_State* const L_) |
| 367 | { | 386 | { |
| 368 | int const _limit{ static_cast<int>(luaL_optinteger(L_, 3, -1)) }; // -1 if we read nil because the argument is absent | 387 | int const _limit{ static_cast<int>(luaL_optinteger(L_, 3, -1)) }; // -1 if we read nil because the argument is absent |
| 369 | push_table(L_, 1); // L_: ud key n? fifos | 388 | push_table(L_, 1); // L_: ud key n? fifos |
| @@ -371,10 +390,10 @@ int keepercall_limit(lua_State* L_) | |||
| 371 | lua_settop(L_, 2); // L_: fifos key | 390 | lua_settop(L_, 2); // L_: fifos key |
| 372 | lua_pushvalue(L_, -1); // L_: fifos key key | 391 | lua_pushvalue(L_, -1); // L_: fifos key key |
| 373 | lua_rawget(L_, -3); // L_: fifos key fifo|nil | 392 | lua_rawget(L_, -3); // L_: fifos key fifo|nil |
| 374 | keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) }; | 393 | KeyUD* _key{ KeyUD::GetPtr(L_, -1) }; |
| 375 | if (_fifo == nullptr) { // L_: fifos key nil | 394 | if (_key == nullptr) { // L_: fifos key nil |
| 376 | lua_pop(L_, 1); // L_: fifos key | 395 | lua_pop(L_, 1); // L_: fifos key |
| 377 | _fifo = fifo_new(KeeperState{ L_ }); // L_: fifos key fifo | 396 | _key = KeyUD::Create(KeeperState{ L_ }); // L_: fifos key fifo |
| 378 | lua_rawset(L_, -3); // L_: fifos | 397 | lua_rawset(L_, -3); // L_: fifos |
| 379 | } | 398 | } |
| 380 | // remove any clutter on the stack | 399 | // remove any clutter on the stack |
| @@ -382,13 +401,13 @@ int keepercall_limit(lua_State* L_) | |||
| 382 | // return true if we decide that blocked threads waiting to write on that key should be awakened | 401 | // return true if we decide that blocked threads waiting to write on that key should be awakened |
| 383 | // this is the case if we detect the key was full but it is no longer the case | 402 | // this is the case if we detect the key was full but it is no longer the case |
| 384 | if ( | 403 | if ( |
| 385 | ((_fifo->limit >= 0) && (_fifo->count >= _fifo->limit)) // the key was full if limited and count exceeded the previous limit | 404 | ((_key->limit >= 0) && (_key->count >= _key->limit)) // the key was full if limited and count exceeded the previous limit |
| 386 | && ((_limit < 0) || (_fifo->count < _limit)) // the key is not full if unlimited or count is lower than the new limit | 405 | && ((_limit < 0) || (_key->count < _limit)) // the key is not full if unlimited or count is lower than the new limit |
| 387 | ) { | 406 | ) { |
| 388 | lua_pushboolean(L_, 1); // L_: true | 407 | lua_pushboolean(L_, 1); // L_: true |
| 389 | } | 408 | } |
| 390 | // set the new limit | 409 | // set the new limit |
| 391 | _fifo->limit = _limit; | 410 | _key->limit = _limit; |
| 392 | // return 0 or 1 value | 411 | // return 0 or 1 value |
| 393 | return lua_gettop(L_); | 412 | return lua_gettop(L_); |
| 394 | } | 413 | } |
| @@ -397,7 +416,7 @@ int keepercall_limit(lua_State* L_) | |||
| 397 | 416 | ||
| 398 | // in: linda_ud key [[val] ...] | 417 | // in: linda_ud key [[val] ...] |
| 399 | // out: true if the linda was full but it's no longer the case, else nothing | 418 | // out: true if the linda was full but it's no longer the case, else nothing |
| 400 | int keepercall_set(lua_State* L_) | 419 | int keepercall_set(lua_State* const L_) |
| 401 | { | 420 | { |
| 402 | bool _should_wake_writers{ false }; | 421 | bool _should_wake_writers{ false }; |
| 403 | STACK_GROW(L_, 6); | 422 | STACK_GROW(L_, 6); |
| @@ -411,48 +430,48 @@ int keepercall_set(lua_State* L_) | |||
| 411 | lua_pushvalue(L_, -1); // L_: fifos key key | 430 | lua_pushvalue(L_, -1); // L_: fifos key key |
| 412 | lua_rawget(L_, 1); // L_: fifos key fifo|nil | 431 | lua_rawget(L_, 1); // L_: fifos key fifo|nil |
| 413 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 432 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
| 414 | keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; | 433 | KeyUD* const _key{ KeyUD::GetPtr(L_, -1) }; |
| 415 | if (_fifo != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo | 434 | if (_key != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo |
| 416 | if (_fifo->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it | 435 | if (_key->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it |
| 417 | lua_pop(L_, 1); // L_: fifos key | 436 | lua_pop(L_, 1); // L_: fifos key |
| 418 | lua_pushnil(L_); // L_: fifos key nil | 437 | lua_pushnil(L_); // L_: fifos key nil |
| 419 | lua_rawset(L_, -3); // L_: fifos | 438 | lua_rawset(L_, -3); // L_: fifos |
| 420 | } else { | 439 | } else { |
| 421 | // we create room if the fifo was full but it is no longer the case | 440 | // we create room if the fifo was full but it is no longer the case |
| 422 | _should_wake_writers = (_fifo->limit > 0) && (_fifo->count >= _fifo->limit); | 441 | _should_wake_writers = (_key->limit > 0) && (_key->count >= _key->limit); |
| 423 | lua_remove(L_, -2); // L_: fifos fifo | 442 | lua_remove(L_, -2); // L_: fifos fifo |
| 424 | lua_newtable(L_); // L_: fifos fifo {} | 443 | lua_newtable(L_); // L_: fifos fifo {} |
| 425 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos fifo | 444 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos fifo |
| 426 | _fifo->first = 1; | 445 | _key->first = 1; |
| 427 | _fifo->count = 0; | 446 | _key->count = 0; |
| 428 | } | 447 | } |
| 429 | } | 448 | } |
| 430 | } else { // set/replace contents stored at the specified key? | 449 | } else { // set/replace contents stored at the specified key? |
| 431 | int const _count{ lua_gettop(L_) - 2 }; // number of items we want to store | 450 | int const _count{ lua_gettop(L_) - 2 }; // number of items we want to store |
| 432 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] key | 451 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] key |
| 433 | lua_rawget(L_, 1); // L_: fifos key [val [, ...]] fifo|nil | 452 | lua_rawget(L_, 1); // L_: fifos key [val [, ...]] fifo|nil |
| 434 | keeper_fifo* _fifo{ keeper_fifo::getPtr(L_, -1) }; | 453 | KeyUD* _key{ KeyUD::GetPtr(L_, -1) }; |
| 435 | if (_fifo == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil | 454 | if (_key == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil |
| 436 | // no need to wake writers in that case, because a writer can't wait on an inexistent key | 455 | // no need to wake writers in that case, because a writer can't wait on an inexistent key |
| 437 | lua_pop(L_, 1); // L_: fifos key [val [, ...]] | 456 | lua_pop(L_, 1); // L_: fifos key [val [, ...]] |
| 438 | std::ignore = fifo_new(KeeperState{ L_ }); // L_: fifos key [val [, ...]] fifo | 457 | std::ignore = KeyUD::Create(KeeperState{ L_ }); // L_: fifos key [val [, ...]] fifo |
| 439 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] fifo key | 458 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] fifo key |
| 440 | lua_pushvalue(L_, -2); // L_: fifos key [val [, ...]] fifo key fifo | 459 | lua_pushvalue(L_, -2); // L_: fifos key [val [, ...]] fifo key fifo |
| 441 | lua_rawset(L_, 1); // L_: fifos key [val [, ...]] fifo | 460 | lua_rawset(L_, 1); // L_: fifos key [val [, ...]] fifo |
| 442 | } else { // L_: fifos key [val [, ...]] fifo | 461 | } else { // L_: fifos key [val [, ...]] fifo |
| 443 | // the fifo exists, we just want to update its contents | 462 | // the fifo exists, we just want to update its contents |
| 444 | // we create room if the fifo was full but it is no longer the case | 463 | // we create room if the fifo was full but it is no longer the case |
| 445 | _should_wake_writers = (_fifo->limit > 0) && (_fifo->count >= _fifo->limit) && (_count < _fifo->limit); | 464 | _should_wake_writers = (_key->limit > 0) && (_key->count >= _key->limit) && (_count < _key->limit); |
| 446 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 465 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
| 447 | lua_newtable(L_); // L_: fifos key [val [, ...]] fifo {} | 466 | lua_newtable(L_); // L_: fifos key [val [, ...]] fifo {} |
| 448 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos key [val [, ...]] fifo | 467 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos key [val [, ...]] fifo |
| 449 | _fifo->first = 1; | 468 | _key->first = 1; |
| 450 | _fifo->count = 0; | 469 | _key->count = 0; |
| 451 | } | 470 | } |
| 452 | _fifo = prepare_fifo_access(L_, -1); // L_: fifos key [val [, ...]] fifotbl | 471 | _key = KeyUD::PrepareAccess(L_, -1); // L_: fifos key [val [, ...]] fifotbl |
| 453 | // move the fifo below the values we want to store | 472 | // move the fifo below the values we want to store |
| 454 | lua_insert(L_, 3); // L_: fifos key fifotbl [val [, ...]] | 473 | lua_insert(L_, 3); // L_: fifos key fifotbl [val [, ...]] |
| 455 | fifo_push(L_, _fifo, _count); // L_: fifos key fifotbl | 474 | _key->push(L_, _count); // L_: fifos key fifotbl |
| 456 | } | 475 | } |
| 457 | return _should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0; | 476 | return _should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0; |
| 458 | } | 477 | } |
| @@ -461,7 +480,7 @@ int keepercall_set(lua_State* L_) | |||
| 461 | 480 | ||
| 462 | // in: linda_ud key [count] | 481 | // in: linda_ud key [count] |
| 463 | // out: at most <count> values | 482 | // out: at most <count> values |
| 464 | int keepercall_get(lua_State* L_) | 483 | int keepercall_get(lua_State* const L_) |
| 465 | { | 484 | { |
| 466 | int _count{ 1 }; | 485 | int _count{ 1 }; |
| 467 | if (lua_gettop(L_) == 3) { // L_: ud key count | 486 | if (lua_gettop(L_) == 3) { // L_: ud key count |
| @@ -471,12 +490,12 @@ int keepercall_get(lua_State* L_) | |||
| 471 | push_table(L_, 1); // L_: ud key fifos | 490 | push_table(L_, 1); // L_: ud key fifos |
| 472 | lua_replace(L_, 1); // L_: fifos key | 491 | lua_replace(L_, 1); // L_: fifos key |
| 473 | lua_rawget(L_, 1); // L_: fifos fifo | 492 | lua_rawget(L_, 1); // L_: fifos fifo |
| 474 | keeper_fifo* const _fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos fifotbl | 493 | KeyUD* const _key{ KeyUD::PrepareAccess(L_, -1) }; // L_: fifos fifotbl |
| 475 | if (_fifo != nullptr && _fifo->count > 0) { | 494 | if (_key != nullptr && _key->count > 0) { |
| 476 | lua_remove(L_, 1); // L_: fifotbl | 495 | lua_remove(L_, 1); // L_: fifotbl |
| 477 | _count = std::min(_count, _fifo->count); | 496 | _count = std::min(_count, _key->count); |
| 478 | // read <count> value off the fifo | 497 | // read <count> value off the fifo |
| 479 | fifo_peek(L_, _fifo, _count); // L_: fifotbl ... | 498 | _key->peek(L_, _count); // L_: fifotbl ... |
| 480 | return _count; | 499 | return _count; |
| 481 | } | 500 | } |
| 482 | // no fifo was ever registered for this key, or it is empty | 501 | // no fifo was ever registered for this key, or it is empty |
| @@ -486,7 +505,7 @@ int keepercall_get(lua_State* L_) | |||
| 486 | // ################################################################################################# | 505 | // ################################################################################################# |
| 487 | 506 | ||
| 488 | // in: linda_ud [, key [, ...]] | 507 | // in: linda_ud [, key [, ...]] |
| 489 | int keepercall_count(lua_State* L_) | 508 | int keepercall_count(lua_State* const L_) |
| 490 | { | 509 | { |
| 491 | push_table(L_, 1); // L_: ud keys fifos | 510 | push_table(L_, 1); // L_: ud keys fifos |
| 492 | switch (lua_gettop(L_)) { | 511 | switch (lua_gettop(L_)) { |
| @@ -496,10 +515,10 @@ int keepercall_count(lua_State* L_) | |||
| 496 | lua_replace(L_, 1); // L_: out fifos | 515 | lua_replace(L_, 1); // L_: out fifos |
| 497 | lua_pushnil(L_); // L_: out fifos nil | 516 | lua_pushnil(L_); // L_: out fifos nil |
| 498 | while (lua_next(L_, 2)) { // L_: out fifos key fifo | 517 | while (lua_next(L_, 2)) { // L_: out fifos key fifo |
| 499 | keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; | 518 | KeyUD* const _key{ KeyUD::GetPtr(L_, -1) }; |
| 500 | lua_pop(L_, 1); // L_: out fifos key | 519 | lua_pop(L_, 1); // L_: out fifos key |
| 501 | lua_pushvalue(L_, -1); // L_: out fifos key key | 520 | lua_pushvalue(L_, -1); // L_: out fifos key key |
| 502 | lua_pushinteger(L_, _fifo->count); // L_: out fifos key key count | 521 | lua_pushinteger(L_, _key->count); // L_: out fifos key key count |
| 503 | lua_rawset(L_, -5); // L_: out fifos key | 522 | lua_rawset(L_, -5); // L_: out fifos key |
| 504 | } | 523 | } |
| 505 | lua_pop(L_, 1); // L_: out | 524 | lua_pop(L_, 1); // L_: out |
| @@ -512,8 +531,8 @@ int keepercall_count(lua_State* L_) | |||
| 512 | if (lua_isnil(L_, -1)) { // L_: the key is unknown // L_: fifos nil | 531 | if (lua_isnil(L_, -1)) { // L_: the key is unknown // L_: fifos nil |
| 513 | lua_remove(L_, -2); // L_: nil | 532 | lua_remove(L_, -2); // L_: nil |
| 514 | } else { // the key is known // L_: fifos fifo | 533 | } else { // the key is known // L_: fifos fifo |
| 515 | keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; | 534 | KeyUD* const _key{ KeyUD::GetPtr(L_, -1) }; |
| 516 | lua_pushinteger(L_, _fifo->count); // L_: fifos fifo count | 535 | lua_pushinteger(L_, _key->count); // L_: fifos fifo count |
| 517 | lua_replace(L_, -3); // L_: count fifo | 536 | lua_replace(L_, -3); // L_: count fifo |
| 518 | lua_pop(L_, 1); // L_: count | 537 | lua_pop(L_, 1); // L_: count |
| 519 | } | 538 | } |
| @@ -528,10 +547,10 @@ int keepercall_count(lua_State* L_) | |||
| 528 | while (lua_gettop(L_) > 2) { | 547 | while (lua_gettop(L_) > 2) { |
| 529 | lua_pushvalue(L_, -1); // L_: out fifos keys... key | 548 | lua_pushvalue(L_, -1); // L_: out fifos keys... key |
| 530 | lua_rawget(L_, 2); // L_: out fifos keys... fifo|nil | 549 | lua_rawget(L_, 2); // L_: out fifos keys... fifo|nil |
| 531 | keeper_fifo* const _fifo{ keeper_fifo::getPtr(L_, -1) }; | 550 | KeyUD* const _key{ KeyUD::GetPtr(L_, -1) }; |
| 532 | lua_pop(L_, 1); // L_: out fifos keys... | 551 | lua_pop(L_, 1); // L_: out fifos keys... |
| 533 | if (_fifo != nullptr) { // L_: the key is known | 552 | if (_key != nullptr) { // L_: the key is known |
| 534 | lua_pushinteger(L_, _fifo->count); // L_: out fifos keys... count | 553 | lua_pushinteger(L_, _key->count); // L_: out fifos keys... count |
| 535 | lua_rawset(L_, 1); // L_: out fifos keys... | 554 | lua_rawset(L_, 1); // L_: out fifos keys... |
| 536 | } else { // the key is unknown | 555 | } else { // the key is unknown |
| 537 | lua_pop(L_, 1); // L_: out fifos keys... | 556 | lua_pop(L_, 1); // L_: out fifos keys... |
| @@ -544,30 +563,6 @@ int keepercall_count(lua_State* L_) | |||
| 544 | } | 563 | } |
| 545 | 564 | ||
| 546 | // ################################################################################################# | 565 | // ################################################################################################# |
| 547 | // Keeper API, accessed from linda methods | ||
| 548 | // ################################################################################################# | ||
| 549 | |||
| 550 | Keeper* Linda::acquireKeeper() const | ||
| 551 | { | ||
| 552 | // can be nullptr if this happens during main state shutdown (lanes is being GC'ed -> no keepers) | ||
| 553 | Keeper* const _K{ whichKeeper() }; | ||
| 554 | if (_K) { | ||
| 555 | _K->mutex.lock(); | ||
| 556 | } | ||
| 557 | return _K; | ||
| 558 | } | ||
| 559 | |||
| 560 | // ################################################################################################# | ||
| 561 | |||
| 562 | void Linda::releaseKeeper(Keeper* const K_) const | ||
| 563 | { | ||
| 564 | if (K_) { // can be nullptr if we tried to acquire during shutdown | ||
| 565 | assert(K_ == whichKeeper()); | ||
| 566 | K_->mutex.unlock(); | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | // ################################################################################################# | ||
| 571 | 566 | ||
| 572 | /* | 567 | /* |
| 573 | * Call a function ('func_name') in the keeper state, and pass on the returned | 568 | * Call a function ('func_name') in the keeper state, and pass on the returned |
| @@ -632,7 +627,7 @@ KeeperCallResult keeper_call(KeeperState K_, keeper_api_t func_, lua_State* L_, | |||
| 632 | 627 | ||
| 633 | // ################################################################################################# | 628 | // ################################################################################################# |
| 634 | // ################################################################################################# | 629 | // ################################################################################################# |
| 635 | // Keeper | 630 | // ########################################## Keeper ############################################### |
| 636 | // ################################################################################################# | 631 | // ################################################################################################# |
| 637 | // ################################################################################################# | 632 | // ################################################################################################# |
| 638 | 633 | ||
| @@ -652,7 +647,7 @@ void Keeper::operator delete[](void* p_, Universe* U_) | |||
| 652 | 647 | ||
| 653 | // ################################################################################################# | 648 | // ################################################################################################# |
| 654 | // ################################################################################################# | 649 | // ################################################################################################# |
| 655 | // Keepers | 650 | // ########################################## Keepers ############################################## |
| 656 | // ################################################################################################# | 651 | // ################################################################################################# |
| 657 | // ################################################################################################# | 652 | // ################################################################################################# |
| 658 | 653 | ||
| @@ -666,6 +661,86 @@ void Keepers::DeleteKV::operator()(Keeper* k_) const | |||
| 666 | } | 661 | } |
| 667 | 662 | ||
| 668 | // ################################################################################################# | 663 | // ################################################################################################# |
| 664 | |||
| 665 | void Keepers::close() | ||
| 666 | { | ||
| 667 | if (isClosing.test_and_set(std::memory_order_release)) { | ||
| 668 | assert(false); // should never close more than once in practice | ||
| 669 | return; | ||
| 670 | } | ||
| 671 | |||
| 672 | if (std::holds_alternative<std::monostate>(keeper_array)) { | ||
| 673 | return; | ||
| 674 | } | ||
| 675 | |||
| 676 | auto _closeOneKeeper = [](Keeper& keeper_) { | ||
| 677 | lua_State* const _K{ std::exchange(keeper_.L, KeeperState{ nullptr }) }; | ||
| 678 | if (_K) { | ||
| 679 | lua_close(_K); | ||
| 680 | } | ||
| 681 | return _K ? true : false; | ||
| 682 | }; | ||
| 683 | |||
| 684 | if (std::holds_alternative<Keeper>(keeper_array)) { | ||
| 685 | _closeOneKeeper(std::get<Keeper>(keeper_array)); | ||
| 686 | } else { | ||
| 687 | KV& _kv = std::get<KV>(keeper_array); | ||
| 688 | |||
| 689 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it | ||
| 690 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists | ||
| 691 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success | ||
| 692 | // which is early-outed with a keepers->nbKeepers null-check | ||
| 693 | size_t const _nbKeepers{ std::exchange(_kv.nbKeepers, 0) }; | ||
| 694 | for (size_t const _i : std::ranges::iota_view{ size_t{ 0 }, _nbKeepers }) { | ||
| 695 | if (!_closeOneKeeper(_kv.keepers[_i])) { | ||
| 696 | // detected partial init: destroy only the mutexes that got initialized properly | ||
| 697 | break; | ||
| 698 | } | ||
| 699 | } | ||
| 700 | } | ||
| 701 | |||
| 702 | keeper_array.emplace<std::monostate>(); | ||
| 703 | } | ||
| 704 | |||
| 705 | // ################################################################################################# | ||
| 706 | |||
| 707 | [[nodiscard]] Keeper* Keepers::getKeeper(int idx_) | ||
| 708 | { | ||
| 709 | if (isClosing.test(std::memory_order_acquire)) { | ||
| 710 | return nullptr; | ||
| 711 | } | ||
| 712 | |||
| 713 | if (std::holds_alternative<std::monostate>(keeper_array)) { | ||
| 714 | return nullptr; | ||
| 715 | } | ||
| 716 | |||
| 717 | if (std::holds_alternative<Keeper>(keeper_array)) { | ||
| 718 | return &std::get<Keeper>(keeper_array); | ||
| 719 | } | ||
| 720 | |||
| 721 | return &std::get<KV>(keeper_array).keepers.get()[idx_]; | ||
| 722 | } | ||
| 723 | |||
| 724 | // ################################################################################################# | ||
| 725 | |||
| 726 | [[nodiscard]] int Keepers::getNbKeepers() const | ||
| 727 | { | ||
| 728 | if (isClosing.test(std::memory_order_acquire)) { | ||
| 729 | return 0; | ||
| 730 | } | ||
| 731 | |||
| 732 | if (std::holds_alternative<std::monostate>(keeper_array)) { | ||
| 733 | return 0; | ||
| 734 | } | ||
| 735 | |||
| 736 | if (std::holds_alternative<Keeper>(keeper_array)) { | ||
| 737 | return 1; | ||
| 738 | } | ||
| 739 | |||
| 740 | return static_cast<int>(std::get<KV>(keeper_array).nbKeepers); | ||
| 741 | } | ||
| 742 | |||
| 743 | // ################################################################################################# | ||
| 669 | /* | 744 | /* |
| 670 | * Initialize keeper states | 745 | * Initialize keeper states |
| 671 | * | 746 | * |
| @@ -762,84 +837,3 @@ void Keepers::initialize(Universe& U_, lua_State* L_, int const nbKeepers_, int | |||
| 762 | } | 837 | } |
| 763 | } | 838 | } |
| 764 | } | 839 | } |
| 765 | |||
| 766 | // ################################################################################################# | ||
| 767 | |||
| 768 | void Keepers::close() | ||
| 769 | { | ||
| 770 | if (isClosing.test_and_set(std::memory_order_release)) { | ||
| 771 | assert(false); // should never close more than once in practice | ||
| 772 | return; | ||
| 773 | } | ||
| 774 | |||
| 775 | if (std::holds_alternative<std::monostate>(keeper_array)) { | ||
| 776 | return; | ||
| 777 | } | ||
| 778 | |||
| 779 | auto _closeOneKeeper = [](Keeper& keeper_) | ||
| 780 | { | ||
| 781 | lua_State* const _K{ std::exchange(keeper_.L, KeeperState{ nullptr }) }; | ||
| 782 | if (_K) { | ||
| 783 | lua_close(_K); | ||
| 784 | } | ||
| 785 | return _K ? true : false; | ||
| 786 | }; | ||
| 787 | |||
| 788 | if (std::holds_alternative<Keeper>(keeper_array)) { | ||
| 789 | _closeOneKeeper(std::get<Keeper>(keeper_array)); | ||
| 790 | } else { | ||
| 791 | KV& _kv = std::get<KV>(keeper_array); | ||
| 792 | |||
| 793 | // NOTE: imagine some keeper state N+1 currently holds a linda that uses another keeper N, and a _gc that will make use of it | ||
| 794 | // when keeper N+1 is closed, object is GCed, linda operation is called, which attempts to acquire keeper N, whose Lua state no longer exists | ||
| 795 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success | ||
| 796 | // which is early-outed with a keepers->nbKeepers null-check | ||
| 797 | size_t const _nbKeepers{ std::exchange(_kv.nbKeepers, 0) }; | ||
| 798 | for (size_t const _i : std::ranges::iota_view{ size_t{ 0 }, _nbKeepers }) { | ||
| 799 | if (!_closeOneKeeper(_kv.keepers[_i])) { | ||
| 800 | // detected partial init: destroy only the mutexes that got initialized properly | ||
| 801 | break; | ||
| 802 | } | ||
| 803 | } | ||
| 804 | } | ||
| 805 | |||
| 806 | keeper_array.emplace<std::monostate>(); | ||
| 807 | } | ||
| 808 | |||
| 809 | // ################################################################################################# | ||
| 810 | |||
| 811 | [[nodiscard]] Keeper* Keepers::getKeeper(int idx_) | ||
| 812 | { | ||
| 813 | if (isClosing.test(std::memory_order_acquire)) { | ||
| 814 | return nullptr; | ||
| 815 | } | ||
| 816 | |||
| 817 | if (std::holds_alternative<std::monostate>(keeper_array)) { | ||
| 818 | return nullptr; | ||
| 819 | } | ||
| 820 | |||
| 821 | if (std::holds_alternative<Keeper>(keeper_array)) { | ||
| 822 | return &std::get<Keeper>(keeper_array); | ||
| 823 | } | ||
| 824 | |||
| 825 | return &std::get<KV>(keeper_array).keepers.get()[idx_]; | ||
| 826 | } | ||
| 827 | |||
| 828 | // ################################################################################################# | ||
| 829 | |||
| 830 | [[nodiscard]] int Keepers::getNbKeepers() const | ||
| 831 | { | ||
| 832 | if (isClosing.test(std::memory_order_acquire)) { | ||
| 833 | return 0; | ||
| 834 | } | ||
| 835 | |||
| 836 | if (std::holds_alternative<std::monostate>(keeper_array)) { | ||
| 837 | return 0; | ||
| 838 | } | ||
| 839 | |||
| 840 | if (std::holds_alternative<Keeper>(keeper_array)) { | ||
| 841 | return 1; | ||
| 842 | } | ||
| 843 | |||
| 844 | return static_cast<int>(std::get<KV>(keeper_array).nbKeepers); | ||
| 845 | } | ||
diff --git a/src/linda.cpp b/src/linda.cpp index f8d6cdc..4ab89ab 100644 --- a/src/linda.cpp +++ b/src/linda.cpp | |||
| @@ -106,8 +106,10 @@ template <bool OPT> | |||
| 106 | } | 106 | } |
| 107 | 107 | ||
| 108 | // ################################################################################################# | 108 | // ################################################################################################# |
| 109 | // ################################################################################################# | ||
| 109 | // #################################### Linda implementation ####################################### | 110 | // #################################### Linda implementation ####################################### |
| 110 | // ################################################################################################# | 111 | // ################################################################################################# |
| 112 | // ################################################################################################# | ||
| 111 | 113 | ||
| 112 | Linda::Linda(Universe* U_, LindaGroup group_, std::string_view const& name_) | 114 | Linda::Linda(Universe* U_, LindaGroup group_, std::string_view const& name_) |
| 113 | : DeepPrelude{ LindaFactory::Instance } | 115 | : DeepPrelude{ LindaFactory::Instance } |
| @@ -126,6 +128,18 @@ Linda::~Linda() | |||
| 126 | 128 | ||
| 127 | // ################################################################################################# | 129 | // ################################################################################################# |
| 128 | 130 | ||
| 131 | Keeper* Linda::acquireKeeper() const | ||
| 132 | { | ||
| 133 | // can be nullptr if this happens during main state shutdown (lanes is being GC'ed -> no keepers) | ||
| 134 | Keeper* const _K{ whichKeeper() }; | ||
| 135 | if (_K) { | ||
| 136 | _K->mutex.lock(); | ||
| 137 | } | ||
| 138 | return _K; | ||
| 139 | } | ||
| 140 | |||
| 141 | // ################################################################################################# | ||
| 142 | |||
| 129 | void Linda::freeAllocatedName() | 143 | void Linda::freeAllocatedName() |
| 130 | { | 144 | { |
| 131 | if (std::holds_alternative<std::string_view>(nameVariant)) { | 145 | if (std::holds_alternative<std::string_view>(nameVariant)) { |
| @@ -186,6 +200,16 @@ int Linda::ProtectedCall(lua_State* L_, lua_CFunction f_) | |||
| 186 | 200 | ||
| 187 | // ################################################################################################# | 201 | // ################################################################################################# |
| 188 | 202 | ||
| 203 | void Linda::releaseKeeper(Keeper* const K_) const | ||
| 204 | { | ||
| 205 | if (K_) { // can be nullptr if we tried to acquire during shutdown | ||
| 206 | assert(K_ == whichKeeper()); | ||
| 207 | K_->mutex.unlock(); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | // ################################################################################################# | ||
| 212 | |||
| 189 | void Linda::setName(std::string_view const& name_) | 213 | void Linda::setName(std::string_view const& name_) |
| 190 | { | 214 | { |
| 191 | // keep default | 215 | // keep default |
| @@ -210,8 +234,10 @@ void Linda::setName(std::string_view const& name_) | |||
| 210 | } | 234 | } |
| 211 | 235 | ||
| 212 | // ################################################################################################# | 236 | // ################################################################################################# |
| 237 | // ################################################################################################# | ||
| 213 | // ########################################## Lua API ############################################## | 238 | // ########################################## Lua API ############################################## |
| 214 | // ################################################################################################# | 239 | // ################################################################################################# |
| 240 | // ################################################################################################# | ||
| 215 | 241 | ||
| 216 | /* | 242 | /* |
| 217 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") | 243 | * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") |
diff --git a/src/linda.h b/src/linda.h index 5753258..8db380a 100644 --- a/src/linda.h +++ b/src/linda.h | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | 2 | ||
| 3 | #include "cancel.h" | 3 | #include "cancel.h" |
| 4 | #include "deep.h" | 4 | #include "deep.h" |
| 5 | #include "keeper.h" | ||
| 6 | #include "universe.h" | 5 | #include "universe.h" |
| 7 | 6 | ||
| 8 | #include <array> | 7 | #include <array> |
diff --git a/tests/keeper.lua b/tests/keeper.lua index 2d432f4..0220eba 100644 --- a/tests/keeper.lua +++ b/tests/keeper.lua | |||
| @@ -31,7 +31,7 @@ end | |||
| 31 | -- ################################################################################################# | 31 | -- ################################################################################################# |
| 32 | -- ################################################################################################# | 32 | -- ################################################################################################# |
| 33 | 33 | ||
| 34 | if false then | 34 | if true then |
| 35 | PRINT "=========================================================================================" | 35 | PRINT "=========================================================================================" |
| 36 | PRINT "Linda groups test:" | 36 | PRINT "Linda groups test:" |
| 37 | 37 | ||
| @@ -55,7 +55,7 @@ DONE() | |||
| 55 | 55 | ||
| 56 | -- ################################################################################################# | 56 | -- ################################################################################################# |
| 57 | 57 | ||
| 58 | if false then | 58 | if true then |
| 59 | PRINT "=========================================================================================" | 59 | PRINT "=========================================================================================" |
| 60 | PRINT "Linda names test:" | 60 | PRINT "Linda names test:" |
| 61 | local unnamedLinda1 = lanes.linda(1) | 61 | local unnamedLinda1 = lanes.linda(1) |
| @@ -102,7 +102,7 @@ DONE() | |||
| 102 | 102 | ||
| 103 | -- ################################################################################################# | 103 | -- ################################################################################################# |
| 104 | 104 | ||
| 105 | if false then | 105 | if true then |
| 106 | PRINT "=========================================================================================" | 106 | PRINT "=========================================================================================" |
| 107 | PRINT "General test:" | 107 | PRINT "General test:" |
| 108 | 108 | ||
