diff options
Diffstat (limited to '')
-rw-r--r-- | src/keeper.cpp | 655 |
1 files changed, 289 insertions, 366 deletions
diff --git a/src/keeper.cpp b/src/keeper.cpp index cbdd0c9..5350d26 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp | |||
@@ -56,7 +56,6 @@ | |||
56 | class keeper_fifo | 56 | class keeper_fifo |
57 | { | 57 | { |
58 | public: | 58 | public: |
59 | |||
60 | int first{ 1 }; | 59 | int first{ 1 }; |
61 | int count{ 0 }; | 60 | int count{ 0 }; |
62 | int limit{ -1 }; | 61 | int limit{ -1 }; |
@@ -81,8 +80,7 @@ static constexpr int kContentsTableIndex{ 1 }; | |||
81 | [[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L_, int idx_) | 80 | [[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L_, int idx_) |
82 | { | 81 | { |
83 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, idx_) }; | 82 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, idx_) }; |
84 | if (fifo != nullptr) | 83 | if (fifo != nullptr) { |
85 | { | ||
86 | idx_ = lua_absindex(L_, idx_); | 84 | idx_ = lua_absindex(L_, idx_); |
87 | STACK_GROW(L_, 1); | 85 | STACK_GROW(L_, 1); |
88 | // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around | 86 | // we can replace the fifo userdata in the stack without fear of it being GCed, there are other references around |
@@ -117,8 +115,7 @@ static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
117 | int const idx{ lua_gettop(L_) - count_ }; | 115 | int const idx{ lua_gettop(L_) - count_ }; |
118 | int const start{ fifo_->first + fifo_->count - 1 }; | 116 | int const start{ fifo_->first + fifo_->count - 1 }; |
119 | // pop all additional arguments, storing them in the fifo | 117 | // pop all additional arguments, storing them in the fifo |
120 | for (int i = count_; i >= 1; --i) | 118 | for (int i = count_; i >= 1; --i) { |
121 | { | ||
122 | // store in the fifo the value at the top of the stack at the specified index, popping it from the stack | 119 | // store in the fifo the value at the top of the stack at the specified index, popping it from the stack |
123 | lua_rawseti(L_, idx, start + i); | 120 | lua_rawseti(L_, idx, start + i); |
124 | } | 121 | } |
@@ -135,8 +132,7 @@ static void fifo_push(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
135 | static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_) | 132 | static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_) |
136 | { | 133 | { |
137 | STACK_GROW(L_, count_); | 134 | STACK_GROW(L_, count_); |
138 | for (int i = 0; i < count_; ++i) | 135 | for (int i = 0; i < count_; ++i) { |
139 | { | ||
140 | lua_rawgeti(L_, 1, (fifo_->first + i)); | 136 | lua_rawgeti(L_, 1, (fifo_->first + i)); |
141 | } | 137 | } |
142 | } | 138 | } |
@@ -145,29 +141,28 @@ static void fifo_peek(lua_State* L_, keeper_fifo* fifo_, int count_) | |||
145 | 141 | ||
146 | // in: fifo | 142 | // in: fifo |
147 | // out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) | 143 | // out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) |
148 | static void fifo_pop( lua_State* L_, keeper_fifo* fifo_, int count_) | 144 | static void fifo_pop(lua_State* L_, keeper_fifo* fifo_, int count_) |
149 | { | 145 | { |
150 | LUA_ASSERT(L_, lua_istable(L_, -1)); | 146 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
151 | int const fifo_idx{ lua_gettop(L_) }; // ... fifotbl | 147 | int const fifo_idx{ lua_gettop(L_) }; // L_: ... fifotbl |
152 | // each iteration pushes a value on the stack! | 148 | // each iteration pushes a value on the stack! |
153 | STACK_GROW(L_, count_ + 2); | 149 | STACK_GROW(L_, count_ + 2); |
154 | // skip first item, we will push it last | 150 | // skip first item, we will push it last |
155 | for (int i = 1; i < count_; ++i) | 151 | for (int i = 1; i < count_; ++i) { |
156 | { | ||
157 | int const at{ fifo_->first + i }; | 152 | int const at{ fifo_->first + i }; |
158 | // push item on the stack | 153 | // push item on the stack |
159 | lua_rawgeti(L_, fifo_idx, at); // ... fifotbl val | 154 | lua_rawgeti(L_, fifo_idx, at); // L_: ... fifotbl val |
160 | // remove item from the fifo | 155 | // remove item from the fifo |
161 | lua_pushnil(L_); // ... fifotbl val nil | 156 | lua_pushnil(L_); // L_: ... fifotbl val nil |
162 | lua_rawseti(L_, fifo_idx, at); // ... fifotbl val | 157 | lua_rawseti(L_, fifo_idx, at); // L_: ... fifotbl val |
163 | } | 158 | } |
164 | // now process first item | 159 | // now process first item |
165 | { | 160 | { |
166 | int const at{ fifo_->first }; | 161 | int const at{ fifo_->first }; |
167 | lua_rawgeti(L_, fifo_idx, at); // ... fifotbl vals val | 162 | lua_rawgeti(L_, fifo_idx, at); // L_: ... fifotbl vals val |
168 | lua_pushnil(L_); // ... fifotbl vals val nil | 163 | lua_pushnil(L_); // L_: ... fifotbl vals val nil |
169 | lua_rawseti(L_, fifo_idx, at); // ... fifotbl vals val | 164 | lua_rawseti(L_, fifo_idx, at); // L_: ... fifotbl vals val |
170 | lua_replace(L_, fifo_idx); // ... vals | 165 | lua_replace(L_, fifo_idx); // L_: ... vals |
171 | } | 166 | } |
172 | 167 | ||
173 | // avoid ever-growing indexes by resetting each time we detect the fifo is empty | 168 | // avoid ever-growing indexes by resetting each time we detect the fifo is empty |
@@ -189,20 +184,19 @@ static void push_table(lua_State* L_, int idx_) | |||
189 | STACK_GROW(L_, 5); | 184 | STACK_GROW(L_, 5); |
190 | STACK_CHECK_START_REL(L_, 0); | 185 | STACK_CHECK_START_REL(L_, 0); |
191 | idx_ = lua_absindex(L_, idx_); | 186 | idx_ = lua_absindex(L_, idx_); |
192 | kFifosRegKey.pushValue(L_); // ud fifos | 187 | kFifosRegKey.pushValue(L_); // L_: ud fifos |
193 | lua_pushvalue(L_, idx_); // ud fifos ud | 188 | lua_pushvalue(L_, idx_); // L_: ud fifos ud |
194 | lua_rawget(L_, -2); // ud fifos fifos[ud] | 189 | lua_rawget(L_, -2); // L_: ud fifos fifos[ud] |
195 | STACK_CHECK(L_, 2); | 190 | STACK_CHECK(L_, 2); |
196 | if (lua_isnil(L_, -1)) | 191 | if (lua_isnil(L_, -1)) { |
197 | { | 192 | lua_pop(L_, 1); // L_: ud fifos |
198 | lua_pop(L_, 1); // ud fifos | ||
199 | // add a new fifos table for this linda | 193 | // add a new fifos table for this linda |
200 | lua_newtable(L_); // ud fifos fifos[ud] | 194 | lua_newtable(L_); // L_: ud fifos fifos[ud] |
201 | lua_pushvalue(L_, idx_); // ud fifos fifos[ud] ud | 195 | lua_pushvalue(L_, idx_); // L_: ud fifos fifos[ud] ud |
202 | lua_pushvalue(L_, -2); // ud fifos fifos[ud] ud fifos[ud] | 196 | lua_pushvalue(L_, -2); // L_: ud fifos fifos[ud] ud fifos[ud] |
203 | lua_rawset(L_, -4); // ud fifos fifos[ud] | 197 | lua_rawset(L_, -4); // L_: ud fifos fifos[ud] |
204 | } | 198 | } |
205 | lua_remove(L_, -2); // ud fifos[ud] | 199 | lua_remove(L_, -2); // L_: ud fifos[ud] |
206 | STACK_CHECK(L_, 1); | 200 | STACK_CHECK(L_, 1); |
207 | } | 201 | } |
208 | 202 | ||
@@ -215,47 +209,45 @@ int keeper_push_linda_storage(Linda& linda_, DestState L_) | |||
215 | SourceState const KL{ K ? K->L : nullptr }; | 209 | SourceState const KL{ K ? K->L : nullptr }; |
216 | if (KL == nullptr) | 210 | if (KL == nullptr) |
217 | return 0; | 211 | return 0; |
218 | STACK_GROW(KL, 4); // KEEPER MAIN | 212 | STACK_GROW(KL, 4); |
219 | STACK_CHECK_START_REL(KL, 0); | 213 | STACK_CHECK_START_REL(KL, 0); |
220 | kFifosRegKey.pushValue(KL); // fifos | 214 | kFifosRegKey.pushValue(KL); // KL: fifos L_: |
221 | lua_pushlightuserdata(KL, &linda_); // fifos ud | 215 | lua_pushlightuserdata(KL, &linda_); // KL: fifos ud L_: |
222 | lua_rawget(KL, -2); // fifos storage | 216 | lua_rawget(KL, -2); // KL: fifos storage L_: |
223 | lua_remove(KL, -2); // storage | 217 | lua_remove(KL, -2); // KL: storage L_: |
224 | if (!lua_istable(KL, -1)) | 218 | if (!lua_istable(KL, -1)) { |
225 | { | 219 | lua_pop(KL, 1); // KL: L_: |
226 | lua_pop(KL, 1); // | ||
227 | STACK_CHECK(KL, 0); | 220 | STACK_CHECK(KL, 0); |
228 | return 0; | 221 | return 0; |
229 | } | 222 | } |
230 | // move data from keeper to destination state | 223 | // move data from keeper to destination state |
231 | STACK_GROW(L_, 5); | 224 | STACK_GROW(L_, 5); |
232 | STACK_CHECK_START_REL(L_, 0); | 225 | STACK_CHECK_START_REL(L_, 0); |
233 | lua_newtable(L_); // out | 226 | lua_newtable(L_); // KL: storage L_: out |
234 | InterCopyContext c{ linda_.U, L_, KL, {}, {}, {}, LookupMode::FromKeeper, {} }; | 227 | InterCopyContext c{ linda_.U, L_, KL, {}, {}, {}, LookupMode::FromKeeper, {} }; |
235 | lua_pushnil(KL); // storage nil | 228 | lua_pushnil(KL); // KL: storage nil L_: out |
236 | while (lua_next(KL, -2)) // storage key fifo | 229 | while (lua_next(KL, -2)) { // KL: storage key fifo L_: out |
237 | { | 230 | keeper_fifo* fifo = prepare_fifo_access(KL, -1); // KL: storage key fifotbl L_: out |
238 | keeper_fifo* fifo = prepare_fifo_access(KL, -1); // storage key fifotbl | 231 | lua_pushvalue(KL, -2); // KL: storage key fifotbl key L_: out |
239 | lua_pushvalue(KL, -2); // storage key fifotbl key | 232 | std::ignore = c.inter_move(1); // KL: storage key fifotbl L_: out key |
240 | std::ignore = c.inter_move(1); // storage key fifotbl // out key | ||
241 | STACK_CHECK(L_, 2); | 233 | STACK_CHECK(L_, 2); |
242 | lua_newtable(L_); // out key keyout | 234 | lua_newtable(L_); // KL: storage key L_: out key keyout |
243 | std::ignore = c.inter_move(1); // storage key // out key keyout fifotbl | 235 | std::ignore = c.inter_move(1); // KL: storage key L_: out key keyout fifotbl |
244 | lua_pushinteger(L_, fifo->first); // out key keyout fifotbl first | 236 | lua_pushinteger(L_, fifo->first); // KL: storage key L_: out key keyout fifotbl first |
245 | STACK_CHECK(L_, 5); | 237 | STACK_CHECK(L_, 5); |
246 | lua_setfield(L_, -3, "first"); // out key keyout fifotbl | 238 | lua_setfield(L_, -3, "first"); // KL: storage key L_: out key keyout fifotbl |
247 | lua_pushinteger(L_, fifo->count); // out key keyout fifobtl count | 239 | lua_pushinteger(L_, fifo->count); // KL: storage key L_: out key keyout fifobtl count |
248 | STACK_CHECK(L_, 5); | 240 | STACK_CHECK(L_, 5); |
249 | lua_setfield(L_, -3, "count"); // out key keyout fifotbl | 241 | lua_setfield(L_, -3, "count"); // KL: storage key L_: out key keyout fifotbl |
250 | lua_pushinteger(L_, fifo->limit); // out key keyout fifotbl limit | 242 | lua_pushinteger(L_, fifo->limit); // KL: storage key L_: out key keyout fifotbl limit |
251 | STACK_CHECK(L_, 5); | 243 | STACK_CHECK(L_, 5); |
252 | lua_setfield(L_, -3, "limit"); // out key keyout fifotbl | 244 | lua_setfield(L_, -3, "limit"); // KL: storage key L_: out key keyout fifotbl |
253 | lua_setfield(L_, -2, "fifo"); // out key keyout | 245 | lua_setfield(L_, -2, "fifo"); // KL: storage key L_: out key keyout |
254 | lua_rawset(L_, -3); // out | 246 | lua_rawset(L_, -3); // KL: storage key L_: out |
255 | STACK_CHECK(L_, 1); | 247 | STACK_CHECK(L_, 1); |
256 | } | 248 | } // KL_: storage L_: out |
257 | STACK_CHECK(L_, 1); | 249 | STACK_CHECK(L_, 1); |
258 | lua_pop(KL, 1); // | 250 | lua_pop(KL, 1); // KL: L_: out |
259 | STACK_CHECK(KL, 0); | 251 | STACK_CHECK(KL, 0); |
260 | return 1; | 252 | return 1; |
261 | } | 253 | } |
@@ -267,11 +259,11 @@ int keepercall_clear(lua_State* L_) | |||
267 | { | 259 | { |
268 | STACK_GROW(L_, 3); | 260 | STACK_GROW(L_, 3); |
269 | STACK_CHECK_START_REL(L_, 0); | 261 | STACK_CHECK_START_REL(L_, 0); |
270 | kFifosRegKey.pushValue(L_); // ud fifos | 262 | kFifosRegKey.pushValue(L_); // L_: ud fifos |
271 | lua_pushvalue(L_, 1); // ud fifos ud | 263 | lua_pushvalue(L_, 1); // L_: ud fifos ud |
272 | lua_pushnil(L_); // ud fifos ud nil | 264 | lua_pushnil(L_); // L_: ud fifos ud nil |
273 | lua_rawset(L_, -3); // ud fifos | 265 | lua_rawset(L_, -3); // L_: ud fifos |
274 | lua_pop(L_, 1); // ud | 266 | lua_pop(L_, 1); // L_: ud |
275 | STACK_CHECK(L_, 0); | 267 | STACK_CHECK(L_, 0); |
276 | return 0; | 268 | return 0; |
277 | } | 269 | } |
@@ -283,32 +275,28 @@ int keepercall_clear(lua_State* L_) | |||
283 | int keepercall_send(lua_State* L_) | 275 | int keepercall_send(lua_State* L_) |
284 | { | 276 | { |
285 | int const n{ lua_gettop(L_) - 2 }; | 277 | int const n{ lua_gettop(L_) - 2 }; |
286 | push_table(L_, 1); // ud key ... fifos | 278 | push_table(L_, 1); // L_: ud key ... fifos |
287 | // get the fifo associated to this key in this linda, create it if it doesn't exist | 279 | // get the fifo associated to this key in this linda, create it if it doesn't exist |
288 | lua_pushvalue(L_, 2); // ud key ... fifos key | 280 | lua_pushvalue(L_, 2); // L_: ud key ... fifos key |
289 | lua_rawget(L_, -2); // ud key ... fifos fifo | 281 | lua_rawget(L_, -2); // L_: ud key ... fifos fifo |
290 | if (lua_isnil(L_, -1)) | 282 | if (lua_isnil(L_, -1)) { |
291 | { | 283 | lua_pop(L_, 1); // L_: ud key ... fifos |
292 | lua_pop(L_, 1); // ud key ... fifos | 284 | std::ignore = fifo_new(KeeperState{ L_ }); // L_: ud key ... fifos fifo |
293 | std::ignore = fifo_new(KeeperState{ L_ }); // ud key ... fifos fifo | 285 | lua_pushvalue(L_, 2); // L_: ud key ... fifos fifo key |
294 | lua_pushvalue(L_, 2); // ud key ... fifos fifo key | 286 | lua_pushvalue(L_, -2); // L_: ud key ... fifos fifo key fifo |
295 | lua_pushvalue(L_, -2); // ud key ... fifos fifo key fifo | 287 | lua_rawset(L_, -4); // L_: ud key ... fifos fifo |
296 | lua_rawset(L_, -4); // ud key ... fifos fifo | 288 | } |
297 | } | 289 | lua_remove(L_, -2); // L_: ud key ... fifo |
298 | lua_remove(L_, -2); // ud key ... fifo | ||
299 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; | 290 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; |
300 | if (fifo->limit >= 0 && fifo->count + n > fifo->limit) | 291 | if (fifo->limit >= 0 && fifo->count + n > fifo->limit) { |
301 | { | 292 | lua_settop(L_, 0); // L_: |
302 | lua_settop(L_, 0); // | 293 | lua_pushboolean(L_, 0); // L_:false |
303 | lua_pushboolean(L_, 0); // false | 294 | } else { |
304 | } | 295 | fifo = prepare_fifo_access(L_, -1); // L_: ud fifotbl |
305 | else | 296 | lua_replace(L_, 2); // L_: ud fifotbl ... |
306 | { | 297 | fifo_push(L_, fifo, n); // L_: ud fifotbl |
307 | fifo = prepare_fifo_access(L_, -1); // ud fifotbl | 298 | lua_settop(L_, 0); // L_: |
308 | lua_replace(L_, 2); // ud fifotbl ... | 299 | lua_pushboolean(L_, 1); // L_: true |
309 | fifo_push(L_, fifo, n); // ud fifotbl | ||
310 | lua_settop(L_, 0); // | ||
311 | lua_pushboolean(L_, 1); // true | ||
312 | } | 300 | } |
313 | return 1; | 301 | return 1; |
314 | } | 302 | } |
@@ -320,30 +308,26 @@ int keepercall_send(lua_State* L_) | |||
320 | int keepercall_receive(lua_State* L_) | 308 | int keepercall_receive(lua_State* L_) |
321 | { | 309 | { |
322 | int const top{ lua_gettop(L_) }; | 310 | int const top{ lua_gettop(L_) }; |
323 | push_table(L_, 1); // ud keys fifos | 311 | push_table(L_, 1); // L_: ud keys fifos |
324 | lua_replace(L_, 1); // fifos keys | 312 | lua_replace(L_, 1); // L_: fifos keys |
325 | for (int i = 2; i <= top; ++i) | 313 | for (int i = 2; i <= top; ++i) { |
326 | { | 314 | lua_pushvalue(L_, i); // L_: fifos keys key[i] |
327 | lua_pushvalue(L_, i); // fifos keys key[i] | 315 | lua_rawget(L_, 1); // L_: fifos keys fifo |
328 | lua_rawget(L_, 1); // fifos keys fifo | 316 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos keys fifotbl |
329 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // fifos keys fifotbl | 317 | if (fifo != nullptr && fifo->count > 0) { |
330 | if (fifo != nullptr && fifo->count > 0) | 318 | fifo_pop(L_, fifo, 1); // L_: fifos keys val |
331 | { | 319 | if (!lua_isnil(L_, -1)) { |
332 | fifo_pop(L_, fifo, 1); // fifos keys val | 320 | lua_replace(L_, 1); // L_: val keys |
333 | if (!lua_isnil(L_, -1)) | 321 | lua_settop(L_, i); // L_: val keys key[i] |
334 | { | 322 | if (i != 2) { |
335 | lua_replace(L_, 1); // val keys | 323 | lua_replace(L_, 2); // L_: val key keys |
336 | lua_settop(L_, i); // val keys key[i] | 324 | lua_settop(L_, 2); // L_: val key |
337 | if (i != 2) | ||
338 | { | ||
339 | lua_replace(L_, 2); // val key keys | ||
340 | lua_settop(L_, 2); // val key | ||
341 | } | 325 | } |
342 | lua_insert(L_, 1); // key, val | 326 | lua_insert(L_, 1); // L_: key, val |
343 | return 2; | 327 | return 2; |
344 | } | 328 | } |
345 | } | 329 | } |
346 | lua_settop(L_, top); // data keys | 330 | lua_settop(L_, top); // L_: data keys |
347 | } | 331 | } |
348 | // nothing to receive | 332 | // nothing to receive |
349 | return 0; | 333 | return 0; |
@@ -355,29 +339,23 @@ int keepercall_receive(lua_State* L_) | |||
355 | int keepercall_receive_batched(lua_State* L_) | 339 | int keepercall_receive_batched(lua_State* L_) |
356 | { | 340 | { |
357 | int const min_count{ static_cast<int>(lua_tointeger(L_, 3)) }; | 341 | int const min_count{ static_cast<int>(lua_tointeger(L_, 3)) }; |
358 | if (min_count > 0) | 342 | if (min_count > 0) { |
359 | { | ||
360 | int const max_count{ static_cast<int>(luaL_optinteger(L_, 4, min_count)) }; | 343 | int const max_count{ static_cast<int>(luaL_optinteger(L_, 4, min_count)) }; |
361 | lua_settop(L_, 2); // ud key | 344 | lua_settop(L_, 2); // L_: ud key |
362 | lua_insert(L_, 1); // key ud | 345 | lua_insert(L_, 1); // L_: key ud |
363 | push_table(L_, 2); // key ud fifos | 346 | push_table(L_, 2); // L_: key ud fifos |
364 | lua_remove(L_, 2); // key fifos | 347 | lua_remove(L_, 2); // L_: key fifos |
365 | lua_pushvalue(L_, 1); // key fifos key | 348 | lua_pushvalue(L_, 1); // L_: key fifos key |
366 | lua_rawget(L_, 2); // key fifos fifo | 349 | lua_rawget(L_, 2); // L_: key fifos fifo |
367 | lua_remove(L_, 2); // key fifo | 350 | lua_remove(L_, 2); // L_: key fifo |
368 | keeper_fifo* const fifo{ prepare_fifo_access(L_, 2) }; // key fifotbl | 351 | keeper_fifo* const fifo{ prepare_fifo_access(L_, 2) }; // L_: key fifotbl |
369 | if (fifo != nullptr && fifo->count >= min_count) | 352 | if (fifo != nullptr && fifo->count >= min_count) { |
370 | { | 353 | fifo_pop(L_, fifo, std::min(max_count, fifo->count)); // L_: key ... |
371 | fifo_pop(L_, fifo, std::min( max_count, fifo->count)); // key ... | 354 | } else { |
372 | } | 355 | lua_settop(L_, 0); // L_: |
373 | else | ||
374 | { | ||
375 | lua_settop(L_, 0); // | ||
376 | } | 356 | } |
377 | return lua_gettop(L_); | 357 | return lua_gettop(L_); |
378 | } | 358 | } else { |
379 | else | ||
380 | { | ||
381 | return 0; | 359 | return 0; |
382 | } | 360 | } |
383 | } | 361 | } |
@@ -389,28 +367,26 @@ int keepercall_receive_batched(lua_State* L_) | |||
389 | int keepercall_limit(lua_State* L_) | 367 | int keepercall_limit(lua_State* L_) |
390 | { | 368 | { |
391 | int const limit{ static_cast<int>(lua_tointeger(L_, 3)) }; | 369 | int const limit{ static_cast<int>(lua_tointeger(L_, 3)) }; |
392 | push_table(L_, 1); // ud key n fifos | 370 | push_table(L_, 1); // L_: ud key n fifos |
393 | lua_replace(L_, 1); // fifos key n | 371 | lua_replace(L_, 1); // L_: fifos key n |
394 | lua_pop(L_, 1); // fifos key | 372 | lua_pop(L_, 1); // L_: fifos key |
395 | lua_pushvalue(L_, -1); // fifos key key | 373 | lua_pushvalue(L_, -1); // L_: fifos key key |
396 | lua_rawget(L_, -3); // fifos key fifo|nil | 374 | lua_rawget(L_, -3); // L_: fifos key fifo|nil |
397 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; | 375 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; |
398 | if (fifo == nullptr) | 376 | if (fifo == nullptr) { // L_: fifos key nil |
399 | { // fifos key nil | 377 | lua_pop(L_, 1); // L_: fifos key |
400 | lua_pop(L_, 1); // fifos key | 378 | fifo = fifo_new(KeeperState{ L_ }); // L_: fifos key fifo |
401 | fifo = fifo_new(KeeperState{ L_ }); // fifos key fifo | 379 | lua_rawset(L_, -3); // L_: fifos |
402 | lua_rawset(L_, -3); // fifos | ||
403 | } | 380 | } |
404 | // remove any clutter on the stack | 381 | // remove any clutter on the stack |
405 | lua_settop(L_, 0); | 382 | lua_settop(L_, 0); // L_: |
406 | // return true if we decide that blocked threads waiting to write on that key should be awakened | 383 | // return true if we decide that blocked threads waiting to write on that key should be awakened |
407 | // this is the case if we detect the key was full but it is no longer the case | 384 | // this is the case if we detect the key was full but it is no longer the case |
408 | if ( | 385 | if ( |
409 | ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit | 386 | ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit |
410 | && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit | 387 | && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit |
411 | ) | 388 | ) { |
412 | { | 389 | lua_pushboolean(L_, 1); // L_: true |
413 | lua_pushboolean(L_, 1); // true | ||
414 | } | 390 | } |
415 | // set the new limit | 391 | // set the new limit |
416 | fifo->limit = limit; | 392 | fifo->limit = limit; |
@@ -421,72 +397,63 @@ int keepercall_limit(lua_State* L_) | |||
421 | // ################################################################################################# | 397 | // ################################################################################################# |
422 | 398 | ||
423 | // in: linda_ud key [[val] ...] | 399 | // in: linda_ud key [[val] ...] |
424 | //out: true if the linda was full but it's no longer the case, else nothing | 400 | // out: true if the linda was full but it's no longer the case, else nothing |
425 | int keepercall_set(lua_State* L_) | 401 | int keepercall_set(lua_State* L_) |
426 | { | 402 | { |
427 | bool should_wake_writers{ false }; | 403 | bool should_wake_writers{ false }; |
428 | STACK_GROW(L_, 6); | 404 | STACK_GROW(L_, 6); |
429 | 405 | ||
430 | // retrieve fifos associated with the linda | 406 | // retrieve fifos associated with the linda |
431 | push_table(L_, 1); // ud key [val [, ...]] fifos | 407 | push_table(L_, 1); // L_: ud key [val [, ...]] fifos |
432 | lua_replace(L_, 1); // fifos key [val [, ...]] | 408 | lua_replace(L_, 1); // L_: fifos key [val [, ...]] |
433 | 409 | ||
434 | // make sure we have a value on the stack | 410 | // make sure we have a value on the stack |
435 | if (lua_gettop(L_) == 2) // fifos key | 411 | if (lua_gettop(L_) == 2) { // L_: fifos key |
436 | { | 412 | lua_pushvalue(L_, -1); // L_: fifos key key |
437 | lua_pushvalue(L_, -1); // fifos key key | 413 | lua_rawget(L_, 1); // L_: fifos key fifo|nil |
438 | lua_rawget(L_, 1); // fifos key fifo|nil | ||
439 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 414 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
440 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 415 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
441 | if (fifo != nullptr) // might be nullptr if we set a nonexistent key to nil | 416 | if (fifo != nullptr) { // might be nullptr if we set a nonexistent key to nil // L_: fifos key fifo |
442 | { // fifos key fifo | 417 | if (fifo->limit < 0) { // fifo limit value is the default (unlimited): we can totally remove it |
443 | if (fifo->limit < 0) // fifo limit value is the default (unlimited): we can totally remove it | 418 | lua_pop(L_, 1); // L_: fifos key |
444 | { | 419 | lua_pushnil(L_); // L_: fifos key nil |
445 | lua_pop(L_, 1); // fifos key | 420 | lua_rawset(L_, -3); // L_: fifos |
446 | lua_pushnil(L_); // fifos key nil | 421 | } else { |
447 | lua_rawset(L_, -3); // fifos | ||
448 | } | ||
449 | else | ||
450 | { | ||
451 | // we create room if the fifo was full but it is no longer the case | 422 | // we create room if the fifo was full but it is no longer the case |
452 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); | 423 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit); |
453 | lua_remove(L_, -2); // fifos fifo | 424 | lua_remove(L_, -2); // L_: fifos fifo |
454 | lua_newtable(L_); // fifos fifo {} | 425 | lua_newtable(L_); // L_: fifos fifo {} |
455 | lua_setiuservalue(L_, -2, kContentsTableIndex); // fifos fifo | 426 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos fifo |
456 | fifo->first = 1; | 427 | fifo->first = 1; |
457 | fifo->count = 0; | 428 | fifo->count = 0; |
458 | } | 429 | } |
459 | } | 430 | } |
460 | } | 431 | } else { // set/replace contents stored at the specified key? |
461 | else // set/replace contents stored at the specified key? | 432 | int const count{ lua_gettop(L_) - 2 }; // number of items we want to store |
462 | { | 433 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] key |
463 | int const count{ lua_gettop(L_) - 2 }; // number of items we want to store | 434 | lua_rawget(L_, 1); // L_: fifos key [val [, ...]] fifo|nil |
464 | lua_pushvalue(L_, 2); // fifos key [val [, ...]] key | ||
465 | lua_rawget(L_, 1); // fifos key [val [, ...]] fifo|nil | ||
466 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; | 435 | keeper_fifo* fifo{ keeper_fifo::getPtr(L_, -1) }; |
467 | if (fifo == nullptr) // can be nullptr if we store a value at a new key | 436 | if (fifo == nullptr) { // can be nullptr if we store a value at a new key // fifos key [val [, ...]] nil |
468 | { // fifos key [val [, ...]] nil | ||
469 | // no need to wake writers in that case, because a writer can't wait on an inexistent key | 437 | // no need to wake writers in that case, because a writer can't wait on an inexistent key |
470 | lua_pop(L_, 1); // fifos key [val [, ...]] | 438 | lua_pop(L_, 1); // L_: fifos key [val [, ...]] |
471 | std::ignore = fifo_new(KeeperState{ L_ }); // fifos key [val [, ...]] fifo | 439 | std::ignore = fifo_new(KeeperState{ L_ }); // L_: fifos key [val [, ...]] fifo |
472 | lua_pushvalue(L_, 2); // fifos key [val [, ...]] fifo key | 440 | lua_pushvalue(L_, 2); // L_: fifos key [val [, ...]] fifo key |
473 | lua_pushvalue(L_, -2); // fifos key [val [, ...]] fifo key fifo | 441 | lua_pushvalue(L_, -2); // L_: fifos key [val [, ...]] fifo key fifo |
474 | lua_rawset(L_, 1); // fifos key [val [, ...]] fifo | 442 | lua_rawset(L_, 1); // L_: fifos key [val [, ...]] fifo |
475 | } | 443 | } else { // L_: fifos key [val [, ...]] fifo |
476 | else // the fifo exists, we just want to update its contents | 444 | // the fifo exists, we just want to update its contents |
477 | { // fifos key [val [, ...]] fifo | ||
478 | // we create room if the fifo was full but it is no longer the case | 445 | // we create room if the fifo was full but it is no longer the case |
479 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit); | 446 | should_wake_writers = (fifo->limit > 0) && (fifo->count >= fifo->limit) && (count < fifo->limit); |
480 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! | 447 | // empty the fifo for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! |
481 | lua_newtable(L_); // fifos key [val [, ...]] fifo {} | 448 | lua_newtable(L_); // L_: fifos key [val [, ...]] fifo {} |
482 | lua_setiuservalue(L_, -2, kContentsTableIndex); // fifos key [val [, ...]] fifo | 449 | lua_setiuservalue(L_, -2, kContentsTableIndex); // L_: fifos key [val [, ...]] fifo |
483 | fifo->first = 1; | 450 | fifo->first = 1; |
484 | fifo->count = 0; | 451 | fifo->count = 0; |
485 | } | 452 | } |
486 | fifo = prepare_fifo_access(L_, -1); // fifos key [val [, ...]] fifotbl | 453 | fifo = prepare_fifo_access(L_, -1); // L_: fifos key [val [, ...]] fifotbl |
487 | // move the fifo below the values we want to store | 454 | // move the fifo below the values we want to store |
488 | lua_insert(L_, 3); // fifos key fifotbl [val [, ...]] | 455 | lua_insert(L_, 3); // L_: fifos key fifotbl [val [, ...]] |
489 | fifo_push(L_, fifo, count); // fifos key fifotbl | 456 | fifo_push(L_, fifo, count); // L_: fifos key fifotbl |
490 | } | 457 | } |
491 | return should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0; | 458 | return should_wake_writers ? (lua_pushboolean(L_, 1), 1) : 0; |
492 | } | 459 | } |
@@ -498,21 +465,19 @@ int keepercall_set(lua_State* L_) | |||
498 | int keepercall_get(lua_State* L_) | 465 | int keepercall_get(lua_State* L_) |
499 | { | 466 | { |
500 | int count{ 1 }; | 467 | int count{ 1 }; |
501 | if (lua_gettop(L_) == 3) // ud key count | 468 | if (lua_gettop(L_) == 3) { // L_: ud key count |
502 | { | ||
503 | count = static_cast<int>(lua_tointeger(L_, 3)); | 469 | count = static_cast<int>(lua_tointeger(L_, 3)); |
504 | lua_pop(L_, 1); // ud key | 470 | lua_pop(L_, 1); // L_: ud key |
505 | } | 471 | } |
506 | push_table(L_, 1); // ud key fifos | 472 | push_table(L_, 1); // L_: ud key fifos |
507 | lua_replace(L_, 1); // fifos key | 473 | lua_replace(L_, 1); // L_: fifos key |
508 | lua_rawget(L_, 1); // fifos fifo | 474 | lua_rawget(L_, 1); // L_: fifos fifo |
509 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // fifos fifotbl | 475 | keeper_fifo* const fifo{ prepare_fifo_access(L_, -1) }; // L_: fifos fifotbl |
510 | if (fifo != nullptr && fifo->count > 0) | 476 | if (fifo != nullptr && fifo->count > 0) { |
511 | { | 477 | lua_remove(L_, 1); // L_: fifotbl |
512 | lua_remove(L_, 1); // fifotbl | ||
513 | count = std::min(count, fifo->count); | 478 | count = std::min(count, fifo->count); |
514 | // read <count> value off the fifo | 479 | // read <count> value off the fifo |
515 | fifo_peek(L_, fifo, count); // fifotbl ... | 480 | fifo_peek(L_, fifo, count); // L_: fifotbl ... |
516 | return count; | 481 | return count; |
517 | } | 482 | } |
518 | // no fifo was ever registered for this key, or it is empty | 483 | // no fifo was ever registered for this key, or it is empty |
@@ -524,65 +489,56 @@ int keepercall_get(lua_State* L_) | |||
524 | // in: linda_ud [, key [, ...]] | 489 | // in: linda_ud [, key [, ...]] |
525 | int keepercall_count(lua_State* L_) | 490 | int keepercall_count(lua_State* L_) |
526 | { | 491 | { |
527 | push_table(L_, 1); // ud keys fifos | 492 | push_table(L_, 1); // L_: ud keys fifos |
528 | switch (lua_gettop(L_)) | 493 | switch (lua_gettop(L_)) { |
529 | { | 494 | // no key is specified: return a table giving the count of all known keys |
530 | // no key is specified: return a table giving the count of all known keys | 495 | case 2: // L_: ud fifos |
531 | case 2: // ud fifos | 496 | lua_newtable(L_); // L_: ud fifos out |
532 | lua_newtable(L_); // ud fifos out | 497 | lua_replace(L_, 1); // L_: out fifos |
533 | lua_replace(L_, 1); // out fifos | 498 | lua_pushnil(L_); // L_: out fifos nil |
534 | lua_pushnil(L_); // out fifos nil | 499 | while (lua_next(L_, 2)) { // L_: out fifos key fifo |
535 | while (lua_next(L_, 2)) // out fifos key fifo | ||
536 | { | ||
537 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 500 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
538 | lua_pop(L_, 1); // out fifos key | 501 | lua_pop(L_, 1); // L_: out fifos key |
539 | lua_pushvalue(L_, -1); // out fifos key key | 502 | lua_pushvalue(L_, -1); // L_: out fifos key key |
540 | lua_pushinteger(L_, fifo->count); // out fifos key key count | 503 | lua_pushinteger(L_, fifo->count); // L_: out fifos key key count |
541 | lua_rawset(L_, -5); // out fifos key | 504 | lua_rawset(L_, -5); // L_: out fifos key |
542 | } | 505 | } |
543 | lua_pop(L_, 1); // out | 506 | lua_pop(L_, 1); // L_: out |
544 | break; | 507 | break; |
545 | 508 | ||
546 | // 1 key is specified: return its count | 509 | // 1 key is specified: return its count |
547 | case 3: // ud key fifos | 510 | case 3: // L_: ud key fifos |
548 | lua_replace(L_, 1); // fifos key | 511 | lua_replace(L_, 1); // L_: fifos key |
549 | lua_rawget(L_, -2); // fifos fifo|nil | 512 | lua_rawget(L_, -2); // L_: fifos fifo|nil |
550 | if (lua_isnil(L_, -1)) // the key is unknown | 513 | if (lua_isnil(L_, -1)) { // L_: the key is unknown // L_: fifos nil |
551 | { // fifos nil | 514 | lua_remove(L_, -2); // L_: nil |
552 | lua_remove(L_, -2); // nil | 515 | } else { // the key is known // L_: fifos fifo |
553 | } | ||
554 | else // the key is known | ||
555 | { // fifos fifo | ||
556 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 516 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
557 | lua_pushinteger(L_, fifo->count); // fifos fifo count | 517 | lua_pushinteger(L_, fifo->count); // L_: fifos fifo count |
558 | lua_replace(L_, -3); // count fifo | 518 | lua_replace(L_, -3); // L_: count fifo |
559 | lua_pop(L_, 1); // count | 519 | lua_pop(L_, 1); // L_: count |
560 | } | 520 | } |
561 | break; | 521 | break; |
562 | 522 | ||
563 | // a variable number of keys is specified: return a table of their counts | 523 | // a variable number of keys is specified: return a table of their counts |
564 | default: // ud keys fifos | 524 | default: // ud keys fifos |
565 | lua_newtable(L_); // ud keys... fifos out | 525 | lua_newtable(L_); // L_: ud keys... fifos out |
566 | lua_replace(L_, 1); // out keys... fifos | 526 | lua_replace(L_, 1); // L_: out keys... fifos |
567 | // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable | 527 | // shifts all keys up in the stack. potentially slow if there are a lot of them, but then it should be bearable |
568 | lua_insert(L_, 2); // out fifos keys... | 528 | lua_insert(L_, 2); // L_: out fifos keys... |
569 | while (lua_gettop(L_) > 2) | 529 | while (lua_gettop(L_) > 2) { |
570 | { | 530 | lua_pushvalue(L_, -1); // L_: out fifos keys... key |
571 | lua_pushvalue(L_, -1); // out fifos keys... key | 531 | lua_rawget(L_, 2); // L_: out fifos keys... fifo|nil |
572 | lua_rawget(L_, 2); // out fifos keys... fifo|nil | ||
573 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; | 532 | keeper_fifo* const fifo{ keeper_fifo::getPtr(L_, -1) }; |
574 | lua_pop(L_, 1); // out fifos keys... | 533 | lua_pop(L_, 1); // L_: out fifos keys... |
575 | if (fifo != nullptr) // the key is known | 534 | if (fifo != nullptr) { // L_: the key is known |
576 | { | 535 | lua_pushinteger(L_, fifo->count); // L_: out fifos keys... count |
577 | lua_pushinteger(L_, fifo->count); // out fifos keys... count | 536 | lua_rawset(L_, 1); // L_: out fifos keys... |
578 | lua_rawset(L_, 1); // out fifos keys... | 537 | } else { // the key is unknown |
538 | lua_pop(L_, 1); // L_: out fifos keys... | ||
579 | } | 539 | } |
580 | else // the key is unknown | 540 | } // all keys are exhausted // L_: out fifos |
581 | { | 541 | lua_pop(L_, 1); // L_: out |
582 | lua_pop(L_, 1); // out fifos keys... | ||
583 | } | ||
584 | } // all keys are exhausted // out fifos | ||
585 | lua_pop(L_, 1); // out | ||
586 | } | 542 | } |
587 | LUA_ASSERT(L_, lua_gettop(L_) == 1); | 543 | LUA_ASSERT(L_, lua_gettop(L_) == 1); |
588 | return 1; | 544 | return 1; |
@@ -592,49 +548,40 @@ int keepercall_count(lua_State* L_) | |||
592 | // Keeper API, accessed from linda methods | 548 | // Keeper API, accessed from linda methods |
593 | // ################################################################################################# | 549 | // ################################################################################################# |
594 | 550 | ||
595 | /*---=== Keeper states ===--- | ||
596 | */ | ||
597 | |||
598 | /* | 551 | /* |
599 | * Pool of keeper states | 552 | * Pool of keeper states |
600 | * | 553 | * |
601 | * Access to keeper states is locked (only one OS thread at a time) so the | 554 | * Access to keeper states is locked (only one OS thread at a time) so the |
602 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the | 555 | * bigger the pool, the less chances of unnecessary waits. Lindas map to the |
603 | * keepers randomly, by a hash. | 556 | * keepers randomly, by a hash. |
604 | */ | 557 | */ |
605 | 558 | ||
606 | // called as __gc for the keepers array userdata | 559 | // called as __gc for the keepers array userdata |
607 | void close_keepers(Universe* U) | 560 | void close_keepers(Universe* U_) |
608 | { | 561 | { |
609 | if (U->keepers != nullptr) | 562 | if (U_->keepers != nullptr) { |
610 | { | 563 | int nbKeepers{ U_->keepers->nb_keepers }; |
611 | int nbKeepers = U->keepers->nb_keepers; | ||
612 | // 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 | 564 | // 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 |
613 | // 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 | 565 | // 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 |
614 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success | 566 | // in that case, the linda operation should do nothing. which means that these operations must check for keeper acquisition success |
615 | // which is early-outed with a U->keepers->nbKeepers null-check | 567 | // which is early-outed with a U->keepers->nbKeepers null-check |
616 | U->keepers->nb_keepers = 0; | 568 | U_->keepers->nb_keepers = 0; |
617 | for (int i = 0; i < nbKeepers; ++i) | 569 | for (int i = 0; i < nbKeepers; ++i) { |
618 | { | 570 | lua_State* const K{ U_->keepers->keeper_array[i].L }; |
619 | lua_State* K = U->keepers->keeper_array[i].L; | 571 | U_->keepers->keeper_array[i].L = KeeperState{ nullptr }; |
620 | U->keepers->keeper_array[i].L = KeeperState{ nullptr }; | 572 | if (K != nullptr) { |
621 | if (K != nullptr) | ||
622 | { | ||
623 | lua_close(K); | 573 | lua_close(K); |
624 | } | 574 | } else { |
625 | else | ||
626 | { | ||
627 | // detected partial init: destroy only the mutexes that got initialized properly | 575 | // detected partial init: destroy only the mutexes that got initialized properly |
628 | nbKeepers = i; | 576 | nbKeepers = i; |
629 | } | 577 | } |
630 | } | 578 | } |
631 | for (int i = 0; i < nbKeepers; ++i) | 579 | for (int i = 0; i < nbKeepers; ++i) { |
632 | { | 580 | U_->keepers->keeper_array[i].~Keeper(); |
633 | U->keepers->keeper_array[i].~Keeper(); | ||
634 | } | 581 | } |
635 | // free the keeper bookkeeping structure | 582 | // free the keeper bookkeeping structure |
636 | U->internal_allocator.free(U->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper)); | 583 | U_->internal_allocator.free(U_->keepers, sizeof(Keepers) + (nbKeepers - 1) * sizeof(Keeper)); |
637 | U->keepers = nullptr; | 584 | U_->keepers = nullptr; |
638 | } | 585 | } |
639 | } | 586 | } |
640 | 587 | ||
@@ -651,98 +598,90 @@ void close_keepers(Universe* U) | |||
651 | * function never fails. | 598 | * function never fails. |
652 | * settings table is expected at position 1 on the stack | 599 | * settings table is expected at position 1 on the stack |
653 | */ | 600 | */ |
654 | void init_keepers(Universe* U, lua_State* L_) | 601 | void init_keepers(Universe* U_, lua_State* L_) |
655 | { | 602 | { |
656 | LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1)); | 603 | LUA_ASSERT(L_, lua_gettop(L_) == 1 && lua_istable(L_, 1)); |
657 | STACK_CHECK_START_REL(L_, 0); // L_ K | 604 | STACK_CHECK_START_REL(L_, 0); // L_: settings |
658 | lua_getfield(L_, 1, "nb_keepers"); // settings nb_keepers | 605 | lua_getfield(L_, 1, "nb_keepers"); // L_: settings nb_keepers |
659 | int const nb_keepers{ static_cast<int>(lua_tointeger(L_, -1)) }; | 606 | int const nb_keepers{ static_cast<int>(lua_tointeger(L_, -1)) }; |
660 | lua_pop(L_, 1); // settings | 607 | lua_pop(L_, 1); // L_: settings |
661 | if (nb_keepers < 1) | 608 | if (nb_keepers < 1) { |
662 | { | ||
663 | raise_luaL_error(L_, "Bad number of keepers (%d)", nb_keepers); | 609 | raise_luaL_error(L_, "Bad number of keepers (%d)", nb_keepers); |
664 | } | 610 | } |
665 | STACK_CHECK(L_, 0); | 611 | STACK_CHECK(L_, 0); |
666 | 612 | ||
667 | lua_getfield(L_, 1, "keepers_gc_threshold"); // settings keepers_gc_threshold | 613 | lua_getfield(L_, 1, "keepers_gc_threshold"); // L_: settings keepers_gc_threshold |
668 | int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) }; | 614 | int const keepers_gc_threshold{ static_cast<int>(lua_tointeger(L_, -1)) }; |
669 | lua_pop(L_, 1); // settings | 615 | lua_pop(L_, 1); // L_: settings |
670 | STACK_CHECK(L_, 0); | 616 | STACK_CHECK(L_, 0); |
671 | 617 | ||
672 | // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states | 618 | // Keepers contains an array of 1 Keeper, adjust for the actual number of keeper states |
673 | { | 619 | { |
674 | size_t const bytes = sizeof(Keepers) + (nb_keepers - 1) * sizeof(Keeper); | 620 | size_t const bytes = sizeof(Keepers) + (nb_keepers - 1) * sizeof(Keeper); |
675 | U->keepers = static_cast<Keepers*>(U->internal_allocator.alloc(bytes)); | 621 | U_->keepers = static_cast<Keepers*>(U_->internal_allocator.alloc(bytes)); |
676 | if (U->keepers == nullptr) | 622 | if (U_->keepers == nullptr) { |
677 | { | ||
678 | raise_luaL_error(L_, "init_keepers() failed while creating keeper array; out of memory"); | 623 | raise_luaL_error(L_, "init_keepers() failed while creating keeper array; out of memory"); |
679 | } | 624 | } |
680 | U->keepers->Keepers::Keepers(); | 625 | U_->keepers->Keepers::Keepers(); |
681 | U->keepers->gc_threshold = keepers_gc_threshold; | 626 | U_->keepers->gc_threshold = keepers_gc_threshold; |
682 | U->keepers->nb_keepers = nb_keepers; | 627 | U_->keepers->nb_keepers = nb_keepers; |
683 | 628 | ||
684 | for (int i = 0; i < nb_keepers; ++i) | 629 | for (int i = 0; i < nb_keepers; ++i) { |
685 | { | 630 | U_->keepers->keeper_array[i].Keeper::Keeper(); |
686 | U->keepers->keeper_array[i].Keeper::Keeper(); | ||
687 | } | 631 | } |
688 | } | 632 | } |
689 | for (int i = 0; i < nb_keepers; ++i) // settings | 633 | for (int i = 0; i < nb_keepers; ++i) { |
690 | { | ||
691 | // note that we will leak K if we raise an error later | 634 | // note that we will leak K if we raise an error later |
692 | KeeperState const K{ create_state(U, L_) }; | 635 | KeeperState const K{ create_state(U_, L_) }; // L_: settings K: |
693 | if (K == nullptr) | 636 | if (K == nullptr) { |
694 | { | ||
695 | raise_luaL_error(L_, "init_keepers() failed while creating keeper states; out of memory"); | 637 | raise_luaL_error(L_, "init_keepers() failed while creating keeper states; out of memory"); |
696 | } | 638 | } |
697 | 639 | ||
698 | U->keepers->keeper_array[i].L = K; | 640 | U_->keepers->keeper_array[i].L = K; |
699 | 641 | ||
700 | if (U->keepers->gc_threshold >= 0) | 642 | if (U_->keepers->gc_threshold >= 0) { |
701 | { | ||
702 | lua_gc(K, LUA_GCSTOP, 0); | 643 | lua_gc(K, LUA_GCSTOP, 0); |
703 | } | 644 | } |
704 | 645 | ||
705 | STACK_CHECK_START_ABS(K, 0); | 646 | STACK_CHECK_START_ABS(K, 0); |
706 | 647 | ||
707 | // copy the universe pointer in the keeper itself | 648 | // copy the universe pointer in the keeper itself |
708 | universe_store(K, U); | 649 | universe_store(K, U_); |
709 | STACK_CHECK(K, 0); | 650 | STACK_CHECK(K, 0); |
710 | 651 | ||
711 | // make sure 'package' is initialized in keeper states, so that we have require() | 652 | // make sure 'package' is initialized in keeper states, so that we have require() |
712 | // this because this is needed when transferring deep userdata object | 653 | // this because this is needed when transferring deep userdata object |
713 | luaL_requiref(K, "package", luaopen_package, 1); // settings package | 654 | luaL_requiref(K, "package", luaopen_package, 1); // L_: settings K: package |
714 | lua_pop(K, 1); // settings | 655 | lua_pop(K, 1); // L_: settings K: |
715 | STACK_CHECK(K, 0); | 656 | STACK_CHECK(K, 0); |
716 | serialize_require(DEBUGSPEW_PARAM_COMMA(U) K); | 657 | serialize_require(DEBUGSPEW_PARAM_COMMA(U_) K); |
717 | STACK_CHECK(K, 0); | 658 | STACK_CHECK(K, 0); |
718 | 659 | ||
719 | // copy package.path and package.cpath from the source state (TODO: use _R._LOADED.package instead of _G.package) | 660 | // copy package.path and package.cpath from the source state (TODO: use _R._LOADED.package instead of _G.package) |
720 | lua_getglobal(L_, "package"); // settings package | 661 | lua_getglobal(L_, "package"); // L_: settings package K: |
721 | if (!lua_isnil(L_, -1)) | 662 | if (!lua_isnil(L_, -1)) { |
722 | { | ||
723 | // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately | 663 | // when copying with mode LookupMode::ToKeeper, error message is pushed at the top of the stack, not raised immediately |
724 | InterCopyContext c{ U, DestState{ K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} }; | 664 | InterCopyContext c{ U_, DestState{ K }, SourceState{ L_ }, {}, SourceIndex{ lua_absindex(L_, -1) }, {}, LookupMode::ToKeeper, {} }; |
725 | if (c.inter_copy_package() != InterCopyResult::Success) | 665 | if (c.inter_copy_package() != InterCopyResult::Success) { // L_: settings ... error_msg K: |
726 | { | ||
727 | // if something went wrong, the error message is at the top of the stack | 666 | // if something went wrong, the error message is at the top of the stack |
728 | lua_remove(L_, -2); // settings error_msg | 667 | lua_remove(L_, -2); // L_: settings error_msg |
729 | raise_lua_error(L_); | 668 | raise_lua_error(L_); |
730 | } | 669 | } |
731 | } | 670 | } |
732 | lua_pop(L_, 1); // settings | 671 | lua_pop(L_, 1); // L_: settings K: |
733 | STACK_CHECK(L_, 0); | 672 | STACK_CHECK(L_, 0); |
734 | STACK_CHECK(K, 0); | 673 | STACK_CHECK(K, 0); |
735 | 674 | ||
736 | // attempt to call on_state_create(), if we have one and it is a C function | 675 | // attempt to call on_state_create(), if we have one and it is a C function |
737 | // (only support a C function because we can't transfer executable Lua code in keepers) | 676 | // (only support a C function because we can't transfer executable Lua code in keepers) |
738 | // will raise an error in L_ in case of problem | 677 | // will raise an error in L_ in case of problem |
739 | call_on_state_create(U, K, L_, LookupMode::ToKeeper); | 678 | call_on_state_create(U_, K, L_, LookupMode::ToKeeper); |
740 | 679 | ||
741 | // to see VM name in Decoda debugger | 680 | // to see VM name in Decoda debugger |
742 | lua_pushfstring(K, "Keeper #%d", i + 1); // "Keeper #n" | 681 | lua_pushfstring(K, "Keeper #%d", i + 1); // L_: settings K: "Keeper #n" |
743 | lua_setglobal(K, "decoda_name"); // | 682 | lua_setglobal(K, "decoda_name"); // L_: settings K: |
744 | // create the fifos table in the keeper state | 683 | // create the fifos table in the keeper state |
745 | kFifosRegKey.setValue(K, [](lua_State* L_) { lua_newtable(L_); }); | 684 | kFifosRegKey.setValue(K, [](lua_State* L_) { lua_newtable(L_); }); // L_: settings K: |
746 | STACK_CHECK(K, 0); | 685 | STACK_CHECK(K, 0); |
747 | } | 686 | } |
748 | STACK_CHECK(L_, 0); | 687 | STACK_CHECK(L_, 0); |
@@ -754,8 +693,7 @@ Keeper* Linda::acquireKeeper() const | |||
754 | { | 693 | { |
755 | int const nbKeepers{ U->keepers->nb_keepers }; | 694 | int const nbKeepers{ U->keepers->nb_keepers }; |
756 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) | 695 | // can be 0 if this happens during main state shutdown (lanes is being GC'ed -> no keepers) |
757 | if (nbKeepers) | 696 | if (nbKeepers) { |
758 | { | ||
759 | Keeper* const K{ &U->keepers->keeper_array[m_keeper_index] }; | 697 | Keeper* const K{ &U->keepers->keeper_array[m_keeper_index] }; |
760 | K->m_mutex.lock(); | 698 | K->m_mutex.lock(); |
761 | return K; | 699 | return K; |
@@ -767,8 +705,7 @@ Keeper* Linda::acquireKeeper() const | |||
767 | 705 | ||
768 | void Linda::releaseKeeper(Keeper* K_) const | 706 | void Linda::releaseKeeper(Keeper* K_) const |
769 | { | 707 | { |
770 | if (K_) // can be nullptr if we tried to acquire during shutdown | 708 | if (K_) { // can be nullptr if we tried to acquire during shutdown |
771 | { | ||
772 | assert(K_ == &U->keepers->keeper_array[m_keeper_index]); | 709 | assert(K_ == &U->keepers->keeper_array[m_keeper_index]); |
773 | K_->m_mutex.unlock(); | 710 | K_->m_mutex.unlock(); |
774 | } | 711 | } |
@@ -779,20 +716,14 @@ void Linda::releaseKeeper(Keeper* K_) const | |||
779 | void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_) | 716 | void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mode_) |
780 | { | 717 | { |
781 | int const n{ lua_gettop(L_) }; | 718 | int const n{ lua_gettop(L_) }; |
782 | for (int i = start_; i <= n; ++i) | 719 | for (int i = start_; i <= n; ++i) { |
783 | { | 720 | if (mode_ == LookupMode::ToKeeper) { |
784 | if (mode_ == LookupMode::ToKeeper) | 721 | if (lua_isnil(L_, i)) { |
785 | { | ||
786 | if (lua_isnil(L_, i)) | ||
787 | { | ||
788 | kNilSentinel.pushKey(L_); | 722 | kNilSentinel.pushKey(L_); |
789 | lua_replace(L_, i); | 723 | lua_replace(L_, i); |
790 | } | 724 | } |
791 | } | 725 | } else { |
792 | else | 726 | if (kNilSentinel.equals(L_, i)) { |
793 | { | ||
794 | if (kNilSentinel.equals(L_, i)) | ||
795 | { | ||
796 | lua_pushnil(L_); | 727 | lua_pushnil(L_); |
797 | lua_replace(L_, i); | 728 | lua_replace(L_, i); |
798 | } | 729 | } |
@@ -803,65 +734,57 @@ void keeper_toggle_nil_sentinels(lua_State* L_, int start_, LookupMode const mod | |||
803 | // ################################################################################################# | 734 | // ################################################################################################# |
804 | 735 | ||
805 | /* | 736 | /* |
806 | * Call a function ('func_name') in the keeper state, and pass on the returned | 737 | * Call a function ('func_name') in the keeper state, and pass on the returned |
807 | * values to 'L'. | 738 | * values to 'L'. |
808 | * | 739 | * |
809 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) | 740 | * 'linda': deep Linda pointer (used only as a unique table key, first parameter) |
810 | * 'starting_index': first of the rest of parameters (none if 0) | 741 | * 'starting_index': first of the rest of parameters (none if 0) |
811 | * | 742 | * |
812 | * Returns: number of return values (pushed to 'L'), unset in case of error | 743 | * Returns: number of return values (pushed to 'L'), unset in case of error |
813 | */ | 744 | */ |
814 | KeeperCallResult keeper_call(Universe* U, KeeperState K, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_) | 745 | KeeperCallResult keeper_call(Universe* U_, KeeperState K_, keeper_api_t func_, lua_State* L_, void* linda_, int starting_index_) |
815 | { | 746 | { |
816 | KeeperCallResult result; | 747 | KeeperCallResult result; |
817 | int const args{ starting_index_ ? (lua_gettop(L_) - starting_index_ + 1) : 0 }; | 748 | int const args{ starting_index_ ? (lua_gettop(L_) - starting_index_ + 1) : 0 }; // L: ... args... K_: |
818 | int const top_K{ lua_gettop(K) }; | 749 | int const top_K{ lua_gettop(K_) }; |
819 | // if we didn't do anything wrong, the keeper stack should be clean | 750 | // if we didn't do anything wrong, the keeper stack should be clean |
820 | LUA_ASSERT(L_, lua_gettop(K) == 0); | 751 | LUA_ASSERT(L_, top_K == 0); |
821 | 752 | ||
822 | STACK_GROW(K, 2); | 753 | STACK_GROW(K_, 2); |
823 | PUSH_KEEPER_FUNC(K, func_); // func_ | 754 | PUSH_KEEPER_FUNC(K_, func_); // L: ... args... K_: func_ |
824 | lua_pushlightuserdata(K, linda_); // func_ linda | 755 | lua_pushlightuserdata(K_, linda_); // L: ... args... K_: func_ linda |
825 | if ( | 756 | if ( |
826 | (args == 0) || | 757 | (args == 0) || |
827 | (InterCopyContext{ U, DestState{ K }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(args) == InterCopyResult::Success) | 758 | (InterCopyContext{ U_, DestState{ K_ }, SourceState{ L_ }, {}, {}, {}, LookupMode::ToKeeper, {} }.inter_copy(args) == InterCopyResult::Success) |
828 | ) | 759 | ) { // L: ... args... K_: func_ linda args... |
829 | { // func_ linda args... | 760 | lua_call(K_, 1 + args, LUA_MULTRET); // L: ... args... K_: result... |
830 | lua_call(K, 1 + args, LUA_MULTRET); // result... | 761 | int const retvals{ lua_gettop(K_) - top_K }; |
831 | int const retvals{ lua_gettop(K) - top_K }; | ||
832 | // note that this can raise a lua error while the keeper state (and its mutex) is acquired | 762 | // note that this can raise a lua error while the keeper state (and its mutex) is acquired |
833 | // this may interrupt a lane, causing the destruction of the underlying OS thread | 763 | // this may interrupt a lane, causing the destruction of the underlying OS thread |
834 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function | 764 | // after this, another lane making use of this keeper can get an error code from the mutex-locking function |
835 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) | 765 | // when attempting to grab the mutex again (WINVER <= 0x400 does this, but locks just fine, I don't know about pthread) |
836 | if ( | 766 | if ( |
837 | (retvals == 0) || | 767 | (retvals == 0) || |
838 | (InterCopyContext{ U, DestState{ L_ }, SourceState{ K }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success) | 768 | (InterCopyContext{ U_, DestState{ L_ }, SourceState{ K_ }, {}, {}, {}, LookupMode::FromKeeper, {} }.inter_move(retvals) == InterCopyResult::Success) |
839 | ) // K->L_ | 769 | ) { // L: ... args... result... K_: result... |
840 | { | ||
841 | result.emplace(retvals); | 770 | result.emplace(retvals); |
842 | } | 771 | } |
843 | } | 772 | } |
844 | // whatever happens, restore the stack to where it was at the origin | 773 | // whatever happens, restore the stack to where it was at the origin |
845 | lua_settop(K, top_K); | 774 | lua_settop(K_, top_K); // L: ... args... result... K_: |
846 | 775 | ||
847 | // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever | 776 | // don't do this for this particular function, as it is only called during Linda destruction, and we don't want to raise an error, ever |
848 | if (func_ != KEEPER_API(clear)) [[unlikely]] | 777 | if (func_ != KEEPER_API(clear)) [[unlikely]] { |
849 | { | ||
850 | // since keeper state GC is stopped, let's run a step once in a while if required | 778 | // since keeper state GC is stopped, let's run a step once in a while if required |
851 | int const gc_threshold{ U->keepers->gc_threshold }; | 779 | int const gc_threshold{ U_->keepers->gc_threshold }; |
852 | if (gc_threshold == 0) [[unlikely]] | 780 | if (gc_threshold == 0) [[unlikely]] { |
853 | { | 781 | lua_gc(K_, LUA_GCSTEP, 0); |
854 | lua_gc(K, LUA_GCSTEP, 0); | 782 | } else if (gc_threshold > 0) [[likely]] { |
855 | } | 783 | int const gc_usage{ lua_gc(K_, LUA_GCCOUNT, 0) }; |
856 | else if (gc_threshold > 0) [[likely]] | 784 | if (gc_usage >= gc_threshold) { |
857 | { | 785 | lua_gc(K_, LUA_GCCOLLECT, 0); |
858 | int const gc_usage{ lua_gc(K, LUA_GCCOUNT, 0) }; | 786 | int const gc_usage_after{ lua_gc(K_, LUA_GCCOUNT, 0) }; |
859 | if (gc_usage >= gc_threshold) | 787 | if (gc_usage_after > gc_threshold) [[unlikely]] { |
860 | { | ||
861 | lua_gc(K, LUA_GCCOLLECT, 0); | ||
862 | int const gc_usage_after{ lua_gc(K, LUA_GCCOUNT, 0) }; | ||
863 | if (gc_usage_after > gc_threshold) [[unlikely]] | ||
864 | { | ||
865 | raise_luaL_error(L_, "Keeper GC threshold is too low, need at least %d", gc_usage_after); | 788 | raise_luaL_error(L_, "Keeper GC threshold is too low, need at least %d", gc_usage_after); |
866 | } | 789 | } |
867 | } | 790 | } |