aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/linda.cpp679
-rw-r--r--src/uniquekey.h8
2 files changed, 335 insertions, 352 deletions
diff --git a/src/linda.cpp b/src/linda.cpp
index dc5864b..77dc4cb 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -1,5 +1,5 @@
1/* 1/*
2 * LINDA.C Copyright (c) 2018, Benoit Germain 2 * LINDA.CPP Copyright (c) 2018-2024, Benoit Germain
3 * 3 *
4 * Linda deep userdata. 4 * Linda deep userdata.
5*/ 5*/
@@ -30,20 +30,15 @@ THE SOFTWARE.
30=============================================================================== 30===============================================================================
31*/ 31*/
32 32
33#include <stdlib.h>
34#include <string.h>
35#include <assert.h>
36
37#include "threading.h"
38#include "compat.h" 33#include "compat.h"
39#include "tools.h"
40#include "universe.h"
41#include "keeper.h"
42#include "deep.h" 34#include "deep.h"
35#include "keeper.h"
43#include "lanes_private.h" 36#include "lanes_private.h"
37#include "threading.h"
38#include "tools.h"
39#include "universe.h"
44 40
45#include <array> 41#include <array>
46#include <bit>
47#include <variant> 42#include <variant>
48 43
49/* 44/*
@@ -166,11 +161,10 @@ static inline Linda* lua_toLinda(lua_State* L, int idx_)
166 161
167static void check_key_types(lua_State* L, int start_, int end_) 162static void check_key_types(lua_State* L, int start_, int end_)
168{ 163{
169 int i; 164 for (int i{ start_ }; i <= end_; ++i)
170 for( i = start_; i <= end_; ++ i)
171 { 165 {
172 int t = lua_type( L, i); 166 int const t{ lua_type(L, i) };
173 if( t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA) 167 if (t == LUA_TBOOLEAN || t == LUA_TNUMBER || t == LUA_TSTRING || t == LUA_TLIGHTUSERDATA)
174 { 168 {
175 continue; 169 continue;
176 } 170 }
@@ -185,26 +179,27 @@ LUAG_FUNC(linda_protected_call)
185 Linda* const linda{ lua_toLinda<false>(L, 1) }; 179 Linda* const linda{ lua_toLinda<false>(L, 1) };
186 180
187 // acquire the keeper 181 // acquire the keeper
188 Keeper* K = keeper_acquire( linda->U->keepers, linda->hashSeed()); 182 Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) };
189 lua_State* KL = K ? K->L : nullptr; 183 lua_State* const KL{ K ? K->L : nullptr };
190 if( KL == nullptr) return 0; 184 if (KL == nullptr)
185 return 0;
191 186
192 // retrieve the actual function to be called and move it before the arguments 187 // retrieve the actual function to be called and move it before the arguments
193 lua_pushvalue( L, lua_upvalueindex( 1)); 188 lua_pushvalue(L, lua_upvalueindex(1));
194 lua_insert( L, 1); 189 lua_insert(L, 1);
195 // do a protected call 190 // do a protected call
196 int const rc{ lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) }; 191 int const rc{ lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) };
197 192
198 // release the keeper 193 // release the keeper
199 keeper_release( K); 194 keeper_release(K);
200 195
201 // if there was an error, forward it 196 // if there was an error, forward it
202 if( rc != LUA_OK) 197 if (rc != LUA_OK)
203 { 198 {
204 raise_lua_error(L); 199 raise_lua_error(L);
205 } 200 }
206 // return whatever the actual operation provided 201 // return whatever the actual operation provided
207 return lua_gettop( L); 202 return lua_gettop(L);
208} 203}
209 204
210// ################################################################################################# 205// #################################################################################################
@@ -218,7 +213,7 @@ LUAG_FUNC(linda_protected_call)
218* 'false' for timeout (only happens when the queue size is limited) 213* 'false' for timeout (only happens when the queue size is limited)
219* nil, CANCEL_ERROR if cancelled 214* nil, CANCEL_ERROR if cancelled
220*/ 215*/
221LUAG_FUNC( linda_send) 216LUAG_FUNC(linda_send)
222{ 217{
223 Linda* const linda{ lua_toLinda<false>(L, 1) }; 218 Linda* const linda{ lua_toLinda<false>(L, 1) };
224 bool ret{ false }; 219 bool ret{ false };
@@ -227,56 +222,57 @@ LUAG_FUNC( linda_send)
227 time_d timeout = -1.0; 222 time_d timeout = -1.0;
228 int key_i = 2; // index of first key, if timeout not there 223 int key_i = 2; // index of first key, if timeout not there
229 224
230 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 225 if (lua_type(L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
231 { 226 {
232 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); 227 timeout = SIGNAL_TIMEOUT_PREPARE(lua_tonumber(L, 2));
233 ++ key_i; 228 ++key_i;
234 } 229 }
235 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key 230 else if (lua_isnil(L, 2)) // alternate explicit "no timeout" by passing nil before the key
236 { 231 {
237 ++ key_i; 232 ++key_i;
238 } 233 }
239 234
240 bool const as_nil_sentinel{ NIL_SENTINEL.equals(L, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided 235 bool const as_nil_sentinel{ NIL_SENTINEL.equals(L, key_i) }; // if not nullptr, send() will silently send a single nil if nothing is provided
241 if( as_nil_sentinel) 236 if (as_nil_sentinel)
242 { 237 {
243 // the real key to send data to is after the NIL_SENTINEL marker 238 // the real key to send data to is after the NIL_SENTINEL marker
244 ++ key_i; 239 ++key_i;
245 } 240 }
246 241
247 // make sure the key is of a valid type 242 // make sure the key is of a valid type
248 check_key_types( L, key_i, key_i); 243 check_key_types(L, key_i, key_i);
249 244
250 STACK_GROW( L, 1); 245 STACK_GROW(L, 1);
251 246
252 // make sure there is something to send 247 // make sure there is something to send
253 if( lua_gettop( L) == key_i) 248 if (lua_gettop(L) == key_i)
254 { 249 {
255 if( as_nil_sentinel) 250 if (as_nil_sentinel)
256 { 251 {
257 // send a single nil if nothing is provided 252 // send a single nil if nothing is provided
258 NIL_SENTINEL.push(L); 253 NIL_SENTINEL.push(L);
259 } 254 }
260 else 255 else
261 { 256 {
262 return luaL_error( L, "no data to send"); 257 return luaL_error(L, "no data to send");
263 } 258 }
264 } 259 }
265 260
266 // convert nils to some special non-nil sentinel in sent values 261 // convert nils to some special non-nil sentinel in sent values
267 keeper_toggle_nil_sentinels( L, key_i + 1, eLM_ToKeeper); 262 keeper_toggle_nil_sentinels(L, key_i + 1, eLM_ToKeeper);
268 263
269 { 264 {
270 Lane* const s{ get_lane_from_registry(L) }; 265 Lane* const lane{ get_lane_from_registry(L) };
271 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 266 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) };
272 lua_State* KL = K ? K->L : nullptr; 267 lua_State* const KL{ K ? K->L : nullptr };
273 if( KL == nullptr) return 0; 268 if (KL == nullptr)
269 return 0;
274 STACK_CHECK_START_REL(KL, 0); 270 STACK_CHECK_START_REL(KL, 0);
275 for(bool try_again{ true };;) 271 for (bool try_again{ true };;)
276 { 272 {
277 if( s != nullptr) 273 if (lane != nullptr)
278 { 274 {
279 cancel = s->cancel_request; 275 cancel = lane->cancel_request;
280 } 276 }
281 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; 277 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel;
282 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 278 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
@@ -286,60 +282,60 @@ LUAG_FUNC( linda_send)
286 break; 282 break;
287 } 283 }
288 284
289 STACK_CHECK( KL, 0); 285 STACK_CHECK(KL, 0);
290 pushed = keeper_call( linda->U, KL, KEEPER_API( send), L, linda, key_i); 286 pushed = keeper_call(linda->U, KL, KEEPER_API(send), L, linda, key_i);
291 if( pushed < 0) 287 if (pushed < 0)
292 { 288 {
293 break; 289 break;
294 } 290 }
295 ASSERT_L( pushed == 1); 291 ASSERT_L(pushed == 1);
296 292
297 ret = lua_toboolean( L, -1) ? true : false; 293 ret = lua_toboolean(L, -1) ? true : false;
298 lua_pop( L, 1); 294 lua_pop(L, 1);
299 295
300 if( ret) 296 if (ret)
301 { 297 {
302 // Wake up ALL waiting threads 298 // Wake up ALL waiting threads
303 SIGNAL_ALL( &linda->write_happened); 299 SIGNAL_ALL(&linda->write_happened);
304 break; 300 break;
305 } 301 }
306 302
307 // instant timout to bypass the wait syscall 303 // instant timout to bypass the wait syscall
308 if( timeout == 0.0) 304 if (timeout == 0.0)
309 { 305 {
310 break; /* no wait; instant timeout */ 306 break; /* no wait; instant timeout */
311 } 307 }
312 308
313 // storage limit hit, wait until timeout or signalled that we should try again 309 // storage limit hit, wait until timeout or signalled that we should try again
314 { 310 {
315 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 311 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
316 if( s != nullptr) 312 if (lane != nullptr)
317 { 313 {
318 // change status of lane to "waiting" 314 // change status of lane to "waiting"
319 prev_status = s->status; // RUNNING, most likely 315 prev_status = lane->status; // RUNNING, most likely
320 ASSERT_L( prev_status == RUNNING); // but check, just in case 316 ASSERT_L(prev_status == RUNNING); // but check, just in case
321 s->status = WAITING; 317 lane->status = WAITING;
322 ASSERT_L( s->waiting_on == nullptr); 318 ASSERT_L(lane->waiting_on == nullptr);
323 s->waiting_on = &linda->read_happened; 319 lane->waiting_on = &linda->read_happened;
324 } 320 }
325 // could not send because no room: wait until some data was read before trying again, or until timeout is reached 321 // could not send because no room: wait until some data was read before trying again, or until timeout is reached
326 try_again = SIGNAL_WAIT( &linda->read_happened, &K->keeper_cs, timeout); 322 try_again = SIGNAL_WAIT(&linda->read_happened, &K->keeper_cs, timeout);
327 if( s != nullptr) 323 if (lane != nullptr)
328 { 324 {
329 s->waiting_on = nullptr; 325 lane->waiting_on = nullptr;
330 s->status = prev_status; 326 lane->status = prev_status;
331 } 327 }
332 } 328 }
333 } 329 }
334 STACK_CHECK( KL, 0); 330 STACK_CHECK(KL, 0);
335 } 331 }
336 332
337 if( pushed < 0) 333 if (pushed < 0)
338 { 334 {
339 return luaL_error( L, "tried to copy unsupported types"); 335 return luaL_error(L, "tried to copy unsupported types");
340 } 336 }
341 337
342 switch( cancel) 338 switch (cancel)
343 { 339 {
344 case CancelRequest::Soft: 340 case CancelRequest::Soft:
345 // if user wants to soft-cancel, the call returns lanes.cancel_error 341 // if user wants to soft-cancel, the call returns lanes.cancel_error
@@ -351,7 +347,7 @@ LUAG_FUNC( linda_send)
351 raise_cancel_error(L); // raises an error and doesn't return 347 raise_cancel_error(L); // raises an error and doesn't return
352 348
353 default: 349 default:
354 lua_pushboolean( L, ret); // true (success) or false (timeout) 350 lua_pushboolean(L, ret); // true (success) or false (timeout)
355 return 1; 351 return 1;
356 } 352 }
357} 353}
@@ -369,132 +365,130 @@ LUAG_FUNC( linda_send)
369 * returns the actual consumed values, or nil if there weren't enough values to consume 365 * returns the actual consumed values, or nil if there weren't enough values to consume
370 * 366 *
371 */ 367 */
372#define BATCH_SENTINEL "270e6c9d-280f-4983-8fee-a7ecdda01475" 368// xxh64 of string "CANCEL_ERROR" generated at https://www.pelock.com/products/hash-calculator
373LUAG_FUNC( linda_receive) 369static constexpr UniqueKey BATCH_SENTINEL{ 0x2DDFEE0968C62AA7ull };
370LUAG_FUNC(linda_receive)
374{ 371{
375 Linda* const linda{ lua_toLinda<false>(L, 1) }; 372 Linda* const linda{ lua_toLinda<false>(L, 1) };
376 int pushed, expected_pushed_min, expected_pushed_max;
377 CancelRequest cancel{ CancelRequest::None };
378 keeper_api_t keeper_receive;
379
380 time_d timeout = -1.0;
381 int key_i = 2;
382 373
383 if( lua_type( L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion 374 time_d timeout{ -1.0 };
375 int key_i{ 2 };
376
377 if (lua_type(L, 2) == LUA_TNUMBER) // we don't want to use lua_isnumber() because of autocoercion
384 { 378 {
385 timeout = SIGNAL_TIMEOUT_PREPARE( lua_tonumber( L, 2)); 379 timeout = SIGNAL_TIMEOUT_PREPARE(lua_tonumber(L, 2));
386 ++ key_i; 380 ++key_i;
387 } 381 }
388 else if( lua_isnil( L, 2)) // alternate explicit "no timeout" by passing nil before the key 382 else if (lua_isnil(L, 2)) // alternate explicit "no timeout" by passing nil before the key
389 { 383 {
390 ++ key_i; 384 ++key_i;
391 } 385 }
392 386
387 keeper_api_t keeper_receive;
388 int expected_pushed_min{ 0 }, expected_pushed_max{ 0 };
393 // are we in batched mode? 389 // are we in batched mode?
390 BATCH_SENTINEL.push(L);
391 int const is_batched{ lua501_equal(L, key_i, -1) };
392 lua_pop(L, 1);
393 if (is_batched)
394 {
395 // no need to pass linda.batched in the keeper state
396 ++key_i;
397 // make sure the keys are of a valid type
398 check_key_types(L, key_i, key_i);
399 // receive multiple values from a single slot
400 keeper_receive = KEEPER_API(receive_batched);
401 // we expect a user-defined amount of return value
402 expected_pushed_min = (int) luaL_checkinteger(L, key_i + 1);
403 expected_pushed_max = (int) luaL_optinteger(L, key_i + 2, expected_pushed_min);
404 // don't forget to count the key in addition to the values
405 ++expected_pushed_min;
406 ++expected_pushed_max;
407 if (expected_pushed_min > expected_pushed_max)
408 {
409 return luaL_error(L, "batched min/max error");
410 }
411 }
412 else
413 {
414 // make sure the keys are of a valid type
415 check_key_types(L, key_i, lua_gettop(L));
416 // receive a single value, checking multiple slots
417 keeper_receive = KEEPER_API(receive);
418 // we expect a single (value, key) pair of returned values
419 expected_pushed_min = expected_pushed_max = 2;
420 }
421
422 Lane* const lane{ get_lane_from_registry(L) };
423 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) };
424 if (K == nullptr)
425 return 0;
426 CancelRequest cancel{ CancelRequest::None };
427 int pushed{ 0 };
428 for (bool try_again{ true };;)
394 { 429 {
395 int is_batched; 430 if (lane != nullptr)
396 lua_pushliteral( L, BATCH_SENTINEL);
397 is_batched = lua501_equal( L, key_i, -1);
398 lua_pop( L, 1);
399 if( is_batched)
400 { 431 {
401 // no need to pass linda.batched in the keeper state 432 cancel = lane->cancel_request;
402 ++ key_i;
403 // make sure the keys are of a valid type
404 check_key_types( L, key_i, key_i);
405 // receive multiple values from a single slot
406 keeper_receive = KEEPER_API( receive_batched);
407 // we expect a user-defined amount of return value
408 expected_pushed_min = (int)luaL_checkinteger( L, key_i + 1);
409 expected_pushed_max = (int)luaL_optinteger( L, key_i + 2, expected_pushed_min);
410 // don't forget to count the key in addition to the values
411 ++ expected_pushed_min;
412 ++ expected_pushed_max;
413 if( expected_pushed_min > expected_pushed_max)
414 {
415 return luaL_error( L, "batched min/max error");
416 }
417 } 433 }
418 else 434 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel;
435 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything
436 if (!try_again || cancel != CancelRequest::None)
419 { 437 {
420 // make sure the keys are of a valid type 438 pushed = 0;
421 check_key_types( L, key_i, lua_gettop( L)); 439 break;
422 // receive a single value, checking multiple slots
423 keeper_receive = KEEPER_API( receive);
424 // we expect a single (value, key) pair of returned values
425 expected_pushed_min = expected_pushed_max = 2;
426 } 440 }
427 }
428 441
429 { 442 // all arguments of receive() but the first are passed to the keeper's receive function
430 Lane* const s{ get_lane_from_registry(L) }; 443 pushed = keeper_call(linda->U, K->L, keeper_receive, L, linda, key_i);
431 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 444 if (pushed < 0)
432 if( K == nullptr) return 0;
433 for (bool try_again{ true };;)
434 { 445 {
435 if( s != nullptr) 446 break;
436 { 447 }
437 cancel = s->cancel_request; 448 if (pushed > 0)
438 } 449 {
439 cancel = (cancel != CancelRequest::None) ? cancel : linda->simulate_cancel; 450 ASSERT_L(pushed >= expected_pushed_min && pushed <= expected_pushed_max);
440 // if user wants to cancel, or looped because of a timeout, the call returns without sending anything 451 // replace sentinels with real nils
441 if (!try_again || cancel != CancelRequest::None) 452 keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, eLM_FromKeeper);
442 { 453 // To be done from within the 'K' locking area
443 pushed = 0; 454 //
444 break; 455 SIGNAL_ALL(&linda->read_happened);
445 } 456 break;
457 }
446 458
447 // all arguments of receive() but the first are passed to the keeper's receive function 459 if (timeout == 0.0)
448 pushed = keeper_call( linda->U, K->L, keeper_receive, L, linda, key_i); 460 {
449 if( pushed < 0) 461 break; /* instant timeout */
450 { 462 }
451 break;
452 }
453 if( pushed > 0)
454 {
455 ASSERT_L( pushed >= expected_pushed_min && pushed <= expected_pushed_max);
456 // replace sentinels with real nils
457 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
458 // To be done from within the 'K' locking area
459 //
460 SIGNAL_ALL( &linda->read_happened);
461 break;
462 }
463 463
464 if( timeout == 0.0) 464 // nothing received, wait until timeout or signalled that we should try again
465 {
466 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings
467 if (lane != nullptr)
465 { 468 {
466 break; /* instant timeout */ 469 // change status of lane to "waiting"
470 prev_status = lane->status; // RUNNING, most likely
471 ASSERT_L(prev_status == RUNNING); // but check, just in case
472 lane->status = WAITING;
473 ASSERT_L(lane->waiting_on == nullptr);
474 lane->waiting_on = &linda->write_happened;
467 } 475 }
468 476 // not enough data to read: wakeup when data was sent, or when timeout is reached
469 // nothing received, wait until timeout or signalled that we should try again 477 try_again = SIGNAL_WAIT(&linda->write_happened, &K->keeper_cs, timeout);
478 if (lane != nullptr)
470 { 479 {
471 enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings 480 lane->waiting_on = nullptr;
472 if( s != nullptr) 481 lane->status = prev_status;
473 {
474 // change status of lane to "waiting"
475 prev_status = s->status; // RUNNING, most likely
476 ASSERT_L( prev_status == RUNNING); // but check, just in case
477 s->status = WAITING;
478 ASSERT_L( s->waiting_on == nullptr);
479 s->waiting_on = &linda->write_happened;
480 }
481 // not enough data to read: wakeup when data was sent, or when timeout is reached
482 try_again = SIGNAL_WAIT( &linda->write_happened, &K->keeper_cs, timeout);
483 if( s != nullptr)
484 {
485 s->waiting_on = nullptr;
486 s->status = prev_status;
487 }
488 } 482 }
489 } 483 }
490 } 484 }
491 485
492 if( pushed < 0) 486 if (pushed < 0)
493 { 487 {
494 return luaL_error( L, "tried to copy unsupported types"); 488 return luaL_error(L, "tried to copy unsupported types");
495 } 489 }
496 490
497 switch( cancel) 491 switch (cancel)
498 { 492 {
499 case CancelRequest::Soft: 493 case CancelRequest::Soft:
500 // if user wants to soft-cancel, the call returns CANCEL_ERROR 494 // if user wants to soft-cancel, the call returns CANCEL_ERROR
@@ -520,53 +514,49 @@ LUAG_FUNC( linda_receive)
520* 514*
521* Existing slot value is replaced, and possible queued entries removed. 515* Existing slot value is replaced, and possible queued entries removed.
522*/ 516*/
523LUAG_FUNC( linda_set) 517LUAG_FUNC(linda_set)
524{ 518{
525 Linda* const linda{ lua_toLinda<false>(L, 1) }; 519 Linda* const linda{ lua_toLinda<false>(L, 1) };
526 int pushed;
527 bool const has_value{ lua_gettop(L) > 2 }; 520 bool const has_value{ lua_gettop(L) > 2 };
528
529 // make sure the key is of a valid type (throws an error if not the case) 521 // make sure the key is of a valid type (throws an error if not the case)
530 check_key_types( L, 2, 2); 522 check_key_types(L, 2, 2);
531 523
524 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) };
525 int pushed{ 0 };
526 if (linda->simulate_cancel == CancelRequest::None)
532 { 527 {
533 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 528 if (has_value)
534 529 {
535 if (linda->simulate_cancel == CancelRequest::None) 530 // convert nils to some special non-nil sentinel in sent values
531 keeper_toggle_nil_sentinels(L, 3, eLM_ToKeeper);
532 }
533 pushed = keeper_call(linda->U, K->L, KEEPER_API(set), L, linda, 2);
534 if (pushed >= 0) // no error?
536 { 535 {
537 if( has_value) 536 ASSERT_L(pushed == 0 || pushed == 1);
537
538 if (has_value)
538 { 539 {
539 // convert nils to some special non-nil sentinel in sent values 540 // we put some data in the slot, tell readers that they should wake
540 keeper_toggle_nil_sentinels( L, 3, eLM_ToKeeper); 541 SIGNAL_ALL(&linda->write_happened); // To be done from within the 'K' locking area
541 } 542 }
542 pushed = keeper_call( linda->U, K->L, KEEPER_API( set), L, linda, 2); 543 if (pushed == 1)
543 if( pushed >= 0) // no error?
544 { 544 {
545 ASSERT_L( pushed == 0 || pushed == 1); 545 // the key was full, but it is no longer the case, tell writers they should wake
546 546 ASSERT_L(lua_type(L, -1) == LUA_TBOOLEAN && lua_toboolean(L, -1) == 1);
547 if( has_value) 547 SIGNAL_ALL(&linda->read_happened); // To be done from within the 'K' locking area
548 {
549 // we put some data in the slot, tell readers that they should wake
550 SIGNAL_ALL( &linda->write_happened); // To be done from within the 'K' locking area
551 }
552 if( pushed == 1)
553 {
554 // the key was full, but it is no longer the case, tell writers they should wake
555 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
556 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
557 }
558 } 548 }
559 } 549 }
560 else // linda is cancelled 550 }
561 { 551 else // linda is cancelled
562 // do nothing and return lanes.cancel_error 552 {
563 CANCEL_ERROR.push(L); 553 // do nothing and return lanes.cancel_error
564 pushed = 1; 554 CANCEL_ERROR.push(L);
565 } 555 pushed = 1;
566 } 556 }
567 557
568 // must trigger any error after keeper state has been released 558 // must trigger any error after keeper state has been released
569 return (pushed < 0) ? luaL_error( L, "tried to copy unsupported types") : pushed; 559 return (pushed < 0) ? luaL_error(L, "tried to copy unsupported types") : pushed;
570} 560}
571 561
572// ################################################################################################# 562// #################################################################################################
@@ -576,21 +566,17 @@ LUAG_FUNC( linda_set)
576 * 566 *
577 * Get a count of the pending elements in the specified keys 567 * Get a count of the pending elements in the specified keys
578 */ 568 */
579LUAG_FUNC( linda_count) 569LUAG_FUNC(linda_count)
580{ 570{
581 Linda* const linda{ lua_toLinda<false>(L, 1) }; 571 Linda* const linda{ lua_toLinda<false>(L, 1) };
582 int pushed;
583
584 // make sure the keys are of a valid type 572 // make sure the keys are of a valid type
585 check_key_types( L, 2, lua_gettop( L)); 573 check_key_types(L, 2, lua_gettop(L));
586 574
575 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) };
576 int const pushed{ keeper_call(linda->U, K->L, KEEPER_API(count), L, linda, 2) };
577 if (pushed < 0)
587 { 578 {
588 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 579 return luaL_error(L, "tried to count an invalid key");
589 pushed = keeper_call( linda->U, K->L, KEEPER_API( count), L, linda, 2);
590 if( pushed < 0)
591 {
592 return luaL_error( L, "tried to count an invalid key");
593 }
594 } 580 }
595 return pushed; 581 return pushed;
596} 582}
@@ -602,39 +588,36 @@ LUAG_FUNC( linda_count)
602* 588*
603* Get one or more values from Linda. 589* Get one or more values from Linda.
604*/ 590*/
605LUAG_FUNC( linda_get) 591LUAG_FUNC(linda_get)
606{ 592{
607 Linda* const linda{ lua_toLinda<false>(L, 1) }; 593 Linda* const linda{ lua_toLinda<false>(L, 1) };
608 int pushed; 594 lua_Integer const count{ luaL_optinteger(L, 3, 1) };
609 lua_Integer count = luaL_optinteger( L, 3, 1); 595 luaL_argcheck(L, count >= 1, 3, "count should be >= 1");
610 luaL_argcheck( L, count >= 1, 3, "count should be >= 1"); 596 luaL_argcheck(L, lua_gettop(L) <= 3, 4, "too many arguments");
611 luaL_argcheck( L, lua_gettop( L) <= 3, 4, "too many arguments");
612
613 // make sure the key is of a valid type (throws an error if not the case) 597 // make sure the key is of a valid type (throws an error if not the case)
614 check_key_types( L, 2, 2); 598 check_key_types(L, 2, 2);
599
600 int pushed{ 0 };
601 if (linda->simulate_cancel == CancelRequest::None)
615 { 602 {
616 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 603 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) };
617 604 pushed = keeper_call(linda->U, K->L, KEEPER_API(get), L, linda, 2);
618 if (linda->simulate_cancel == CancelRequest::None) 605 if (pushed > 0)
619 { 606 {
620 pushed = keeper_call( linda->U, K->L, KEEPER_API( get), L, linda, 2); 607 keeper_toggle_nil_sentinels(L, lua_gettop(L) - pushed, eLM_FromKeeper);
621 if( pushed > 0)
622 {
623 keeper_toggle_nil_sentinels( L, lua_gettop( L) - pushed, eLM_FromKeeper);
624 }
625 }
626 else // linda is cancelled
627 {
628 // do nothing and return lanes.cancel_error
629 CANCEL_ERROR.push(L);
630 pushed = 1;
631 }
632 // an error can be raised if we attempt to read an unregistered function
633 if( pushed < 0)
634 {
635 return luaL_error( L, "tried to copy unsupported types");
636 } 608 }
637 } 609 }
610 else // linda is cancelled
611 {
612 // do nothing and return lanes.cancel_error
613 CANCEL_ERROR.push(L);
614 pushed = 1;
615 }
616 // an error can be raised if we attempt to read an unregistered function
617 if (pushed < 0)
618 {
619 return luaL_error(L, "tried to copy unsupported types");
620 }
638 621
639 return pushed; 622 return pushed;
640} 623}
@@ -650,8 +633,6 @@ LUAG_FUNC( linda_get)
650LUAG_FUNC( linda_limit) 633LUAG_FUNC( linda_limit)
651{ 634{
652 Linda* const linda{ lua_toLinda<false>(L, 1) }; 635 Linda* const linda{ lua_toLinda<false>(L, 1) };
653 int pushed;
654
655 // make sure we got 3 arguments: the linda, a key and a limit 636 // make sure we got 3 arguments: the linda, a key and a limit
656 luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments"); 637 luaL_argcheck( L, lua_gettop( L) == 3, 2, "wrong number of arguments");
657 // make sure we got a numeric limit 638 // make sure we got a numeric limit
@@ -659,26 +640,24 @@ LUAG_FUNC( linda_limit)
659 // make sure the key is of a valid type 640 // make sure the key is of a valid type
660 check_key_types( L, 2, 2); 641 check_key_types( L, 2, 2);
661 642
643 int pushed{ 0 };
644 if (linda->simulate_cancel == CancelRequest::None)
662 { 645 {
663 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) }; 646 Keeper* const K{ which_keeper(linda->U->keepers, linda->hashSeed()) };
664 647 pushed = keeper_call(linda->U, K->L, KEEPER_API(limit), L, linda, 2);
665 if (linda->simulate_cancel == CancelRequest::None) 648 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
666 { 649 if( pushed == 1)
667 pushed = keeper_call( linda->U, K->L, KEEPER_API( limit), L, linda, 2);
668 ASSERT_L( pushed == 0 || pushed == 1); // no error, optional boolean value saying if we should wake blocked writer threads
669 if( pushed == 1)
670 {
671 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
672 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
673 }
674 }
675 else // linda is cancelled
676 { 650 {
677 // do nothing and return lanes.cancel_error 651 ASSERT_L( lua_type( L, -1) == LUA_TBOOLEAN && lua_toboolean( L, -1) == 1);
678 CANCEL_ERROR.push(L); 652 SIGNAL_ALL( &linda->read_happened); // To be done from within the 'K' locking area
679 pushed = 1;
680 } 653 }
681 } 654 }
655 else // linda is cancelled
656 {
657 // do nothing and return lanes.cancel_error
658 CANCEL_ERROR.push(L);
659 pushed = 1;
660 }
682 // propagate pushed boolean if any 661 // propagate pushed boolean if any
683 return pushed; 662 return pushed;
684} 663}
@@ -690,35 +669,34 @@ LUAG_FUNC( linda_limit)
690* 669*
691* Signal linda so that waiting threads wake up as if their own lane was cancelled 670* Signal linda so that waiting threads wake up as if their own lane was cancelled
692*/ 671*/
693LUAG_FUNC( linda_cancel) 672LUAG_FUNC(linda_cancel)
694{ 673{
695 Linda* const linda{ lua_toLinda<false>(L, 1) }; 674 Linda* const linda{ lua_toLinda<false>(L, 1) };
696 char const* who = luaL_optstring( L, 2, "both"); 675 char const* who = luaL_optstring(L, 2, "both");
697
698 // make sure we got 3 arguments: the linda, a key and a limit 676 // make sure we got 3 arguments: the linda, a key and a limit
699 luaL_argcheck( L, lua_gettop( L) <= 2, 2, "wrong number of arguments"); 677 luaL_argcheck(L, lua_gettop(L) <= 2, 2, "wrong number of arguments");
700 678
701 linda->simulate_cancel = CancelRequest::Soft; 679 linda->simulate_cancel = CancelRequest::Soft;
702 if( strcmp( who, "both") == 0) // tell everyone writers to wake up 680 if (strcmp(who, "both") == 0) // tell everyone writers to wake up
703 { 681 {
704 SIGNAL_ALL( &linda->write_happened); 682 SIGNAL_ALL(&linda->write_happened);
705 SIGNAL_ALL( &linda->read_happened); 683 SIGNAL_ALL(&linda->read_happened);
706 } 684 }
707 else if( strcmp( who, "none") == 0) // reset flag 685 else if (strcmp(who, "none") == 0) // reset flag
708 { 686 {
709 linda->simulate_cancel = CancelRequest::None; 687 linda->simulate_cancel = CancelRequest::None;
710 } 688 }
711 else if( strcmp( who, "read") == 0) // tell blocked readers to wake up 689 else if (strcmp(who, "read") == 0) // tell blocked readers to wake up
712 { 690 {
713 SIGNAL_ALL( &linda->write_happened); 691 SIGNAL_ALL(&linda->write_happened);
714 } 692 }
715 else if( strcmp( who, "write") == 0) // tell blocked writers to wake up 693 else if (strcmp(who, "write") == 0) // tell blocked writers to wake up
716 { 694 {
717 SIGNAL_ALL( &linda->read_happened); 695 SIGNAL_ALL(&linda->read_happened);
718 } 696 }
719 else 697 else
720 { 698 {
721 return luaL_error( L, "unknown wake hint '%s'", who); 699 return luaL_error(L, "unknown wake hint '%s'", who);
722 } 700 }
723 return 0; 701 return 0;
724} 702}
@@ -735,10 +713,10 @@ LUAG_FUNC( linda_cancel)
735* different userdata and won't be known to be essentially the same deep one 713* different userdata and won't be known to be essentially the same deep one
736* without this. 714* without this.
737*/ 715*/
738LUAG_FUNC( linda_deep) 716LUAG_FUNC(linda_deep)
739{ 717{
740 Linda* const linda{ lua_toLinda<false>(L, 1) }; 718 Linda* const linda{ lua_toLinda<false>(L, 1) };
741 lua_pushlightuserdata( L, linda); // just the address 719 lua_pushlightuserdata(L, linda); // just the address
742 return 1; 720 return 1;
743} 721}
744 722
@@ -756,21 +734,21 @@ template <bool OPT>
756static int linda_tostring(lua_State* L, int idx_) 734static int linda_tostring(lua_State* L, int idx_)
757{ 735{
758 Linda* const linda{ lua_toLinda<OPT>(L, idx_) }; 736 Linda* const linda{ lua_toLinda<OPT>(L, idx_) };
759 if( linda != nullptr) 737 if (linda != nullptr)
760 { 738 {
761 char text[128]; 739 char text[128];
762 int len; 740 int len;
763 if( linda->getName()) 741 if (linda->getName())
764 len = sprintf( text, "Linda: %.*s", (int)sizeof(text) - 8, linda->getName()); 742 len = sprintf(text, "Linda: %.*s", (int) sizeof(text) - 8, linda->getName());
765 else 743 else
766 len = sprintf( text, "Linda: %p", linda); 744 len = sprintf(text, "Linda: %p", linda);
767 lua_pushlstring( L, text, len); 745 lua_pushlstring(L, text, len);
768 return 1; 746 return 1;
769 } 747 }
770 return 0; 748 return 0;
771} 749}
772 750
773LUAG_FUNC( linda_tostring) 751LUAG_FUNC(linda_tostring)
774{ 752{
775 return linda_tostring<false>(L, 1); 753 return linda_tostring<false>(L, 1);
776} 754}
@@ -784,25 +762,25 @@ LUAG_FUNC( linda_tostring)
784* 762*
785* Useful for concatenation or debugging purposes 763* Useful for concatenation or debugging purposes
786*/ 764*/
787LUAG_FUNC( linda_concat) 765LUAG_FUNC(linda_concat)
788{ // linda1? linda2? 766{ // linda1? linda2?
789 bool atLeastOneLinda{ false }; 767 bool atLeastOneLinda{ false };
790 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. 768 // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both.
791 if( linda_tostring<true>( L, 1)) 769 if (linda_tostring<true>(L, 1))
792 { 770 {
793 atLeastOneLinda = true; 771 atLeastOneLinda = true;
794 lua_replace( L, 1); 772 lua_replace(L, 1);
795 } 773 }
796 if( linda_tostring<true>( L, 2)) 774 if (linda_tostring<true>(L, 2))
797 { 775 {
798 atLeastOneLinda = true; 776 atLeastOneLinda = true;
799 lua_replace( L, 2); 777 lua_replace(L, 2);
800 } 778 }
801 if( !atLeastOneLinda) // should not be possible 779 if (!atLeastOneLinda) // should not be possible
802 { 780 {
803 return luaL_error( L, "internal error: linda_concat called on non-Linda"); 781 return luaL_error(L, "internal error: linda_concat called on non-Linda");
804 } 782 }
805 lua_concat( L, 2); 783 lua_concat(L, 2);
806 return 1; 784 return 1;
807} 785}
808 786
@@ -812,17 +790,19 @@ LUAG_FUNC( linda_concat)
812 * table = linda:dump() 790 * table = linda:dump()
813 * return a table listing all pending data inside the linda 791 * return a table listing all pending data inside the linda
814 */ 792 */
815LUAG_FUNC( linda_dump) 793LUAG_FUNC(linda_dump)
816{ 794{
817 Linda* const linda{ lua_toLinda<false>(L, 1) }; 795 Linda* const linda{ lua_toLinda<false>(L, 1) };
818 return keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()); 796 return keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed());
819} 797}
820 798
799// #################################################################################################
800
821/* 801/*
822 * table = linda:dump() 802 * table = linda:dump()
823 * return a table listing all pending data inside the linda 803 * return a table listing all pending data inside the linda
824 */ 804 */
825LUAG_FUNC( linda_towatch) 805LUAG_FUNC(linda_towatch)
826{ 806{
827 Linda* const linda{ lua_toLinda<false>(L, 1) }; 807 Linda* const linda{ lua_toLinda<false>(L, 1) };
828 int pushed{ keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()) }; 808 int pushed{ keeper_push_linda_storage(linda->U, L, linda, linda->hashSeed()) };
@@ -834,6 +814,8 @@ LUAG_FUNC( linda_towatch)
834 return pushed; 814 return pushed;
835} 815}
836 816
817// #################################################################################################
818
837/* 819/*
838* Identity function of a shared userdata object. 820* Identity function of a shared userdata object.
839* 821*
@@ -868,51 +850,51 @@ static void* linda_id( lua_State* L, DeepOp op_)
868 char const* linda_name = nullptr; 850 char const* linda_name = nullptr;
869 unsigned long linda_group = 0; 851 unsigned long linda_group = 0;
870 // should have a string and/or a number of the stack as parameters (name and group) 852 // should have a string and/or a number of the stack as parameters (name and group)
871 switch( lua_gettop( L)) 853 switch (lua_gettop(L))
872 { 854 {
873 default: // 0 855 default: // 0
874 break; 856 break;
875 857
876 case 1: // 1 parameter, either a name or a group 858 case 1: // 1 parameter, either a name or a group
877 if( lua_type( L, -1) == LUA_TSTRING) 859 if (lua_type(L, -1) == LUA_TSTRING)
878 { 860 {
879 linda_name = lua_tolstring( L, -1, &name_len); 861 linda_name = lua_tolstring(L, -1, &name_len);
880 } 862 }
881 else 863 else
882 { 864 {
883 linda_group = (unsigned long) lua_tointeger( L, -1); 865 linda_group = (unsigned long) lua_tointeger(L, -1);
884 } 866 }
885 break; 867 break;
886 868
887 case 2: // 2 parameters, a name and group, in that order 869 case 2: // 2 parameters, a name and group, in that order
888 linda_name = lua_tolstring( L, -2, &name_len); 870 linda_name = lua_tolstring(L, -2, &name_len);
889 linda_group = (unsigned long) lua_tointeger( L, -1); 871 linda_group = (unsigned long) lua_tointeger(L, -1);
890 break; 872 break;
891 } 873 }
892 874
893 /* The deep data is allocated separately of Lua stack; we might no 875 /* The deep data is allocated separately of Lua stack; we might no
894 * longer be around when last reference to it is being released. 876 * longer be around when last reference to it is being released.
895 * One can use any memory allocation scheme. 877 * One can use any memory allocation scheme.
896 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda 878 * just don't use L's allocF because we don't know which state will get the honor of GCing the linda
897 */ 879 */
898 Universe* const U{ universe_get(L) }; 880 Universe* const U{ universe_get(L) };
899 Linda* s{ new (U) Linda{ U, linda_group, linda_name, name_len } }; 881 Linda* linda{ new (U) Linda{ U, linda_group, linda_name, name_len } };
900 return s; 882 return linda;
901 } 883 }
902 884
903 case eDO_delete: 885 case eDO_delete:
904 { 886 {
905 Linda* const linda{ lua_tolightuserdata<Linda>(L, 1) }; 887 Linda* const linda{ lua_tolightuserdata<Linda>(L, 1) };
906 ASSERT_L( linda); 888 ASSERT_L(linda);
907 889
908 // Clean associated structures in the keeper state. 890 // Clean associated structures in the keeper state.
909 Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) }; 891 Keeper* const K{ keeper_acquire(linda->U->keepers, linda->hashSeed()) };
910 if( K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup) 892 if (K && K->L) // can be nullptr if this happens during main state shutdown (lanes is GC'ed -> no keepers -> no need to cleanup)
911 { 893 {
912 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex... 894 // hopefully this won't ever raise an error as we would jump to the closest pcall site while forgetting to release the keeper mutex...
913 keeper_call( linda->U, K->L, KEEPER_API( clear), L, linda, 0); 895 keeper_call(linda->U, K->L, KEEPER_API(clear), L, linda, 0);
914 } 896 }
915 keeper_release( K); 897 keeper_release(K);
916 898
917 delete linda; // operator delete overload ensures things go as expected 899 delete linda; // operator delete overload ensures things go as expected
918 return nullptr; 900 return nullptr;
@@ -920,73 +902,72 @@ static void* linda_id( lua_State* L, DeepOp op_)
920 902
921 case eDO_metatable: 903 case eDO_metatable:
922 { 904 {
923
924 STACK_CHECK_START_REL(L, 0); 905 STACK_CHECK_START_REL(L, 0);
925 lua_newtable( L); 906 lua_newtable(L);
926 // metatable is its own index 907 // metatable is its own index
927 lua_pushvalue( L, -1); 908 lua_pushvalue(L, -1);
928 lua_setfield( L, -2, "__index"); 909 lua_setfield(L, -2, "__index");
929 910
930 // protect metatable from external access 911 // protect metatable from external access
931 lua_pushliteral( L, "Linda"); 912 lua_pushliteral(L, "Linda");
932 lua_setfield( L, -2, "__metatable"); 913 lua_setfield(L, -2, "__metatable");
933 914
934 lua_pushcfunction( L, LG_linda_tostring); 915 lua_pushcfunction(L, LG_linda_tostring);
935 lua_setfield( L, -2, "__tostring"); 916 lua_setfield(L, -2, "__tostring");
936 917
937 // Decoda __towatch support 918 // Decoda __towatch support
938 lua_pushcfunction( L, LG_linda_towatch); 919 lua_pushcfunction(L, LG_linda_towatch);
939 lua_setfield( L, -2, "__towatch"); 920 lua_setfield(L, -2, "__towatch");
940 921
941 lua_pushcfunction( L, LG_linda_concat); 922 lua_pushcfunction(L, LG_linda_concat);
942 lua_setfield( L, -2, "__concat"); 923 lua_setfield(L, -2, "__concat");
943 924
944 // protected calls, to ensure associated keeper is always released even in case of error 925 // protected calls, to ensure associated keeper is always released even in case of error
945 // all function are the protected call wrapper, where the actual operation is provided as upvalue 926 // all function are the protected call wrapper, where the actual operation is provided as upvalue
946 // note that this kind of thing can break function lookup as we use the function pointer here and there 927 // note that this kind of thing can break function lookup as we use the function pointer here and there
947 928
948 lua_pushcfunction( L, LG_linda_send); 929 lua_pushcfunction(L, LG_linda_send);
949 lua_pushcclosure( L, LG_linda_protected_call, 1); 930 lua_pushcclosure(L, LG_linda_protected_call, 1);
950 lua_setfield( L, -2, "send"); 931 lua_setfield(L, -2, "send");
951 932
952 lua_pushcfunction( L, LG_linda_receive); 933 lua_pushcfunction(L, LG_linda_receive);
953 lua_pushcclosure( L, LG_linda_protected_call, 1); 934 lua_pushcclosure(L, LG_linda_protected_call, 1);
954 lua_setfield( L, -2, "receive"); 935 lua_setfield(L, -2, "receive");
955 936
956 lua_pushcfunction( L, LG_linda_limit); 937 lua_pushcfunction(L, LG_linda_limit);
957 lua_pushcclosure( L, LG_linda_protected_call, 1); 938 lua_pushcclosure(L, LG_linda_protected_call, 1);
958 lua_setfield( L, -2, "limit"); 939 lua_setfield(L, -2, "limit");
959 940
960 lua_pushcfunction( L, LG_linda_set); 941 lua_pushcfunction(L, LG_linda_set);
961 lua_pushcclosure( L, LG_linda_protected_call, 1); 942 lua_pushcclosure(L, LG_linda_protected_call, 1);
962 lua_setfield( L, -2, "set"); 943 lua_setfield(L, -2, "set");
963 944
964 lua_pushcfunction( L, LG_linda_count); 945 lua_pushcfunction(L, LG_linda_count);
965 lua_pushcclosure( L, LG_linda_protected_call, 1); 946 lua_pushcclosure(L, LG_linda_protected_call, 1);
966 lua_setfield( L, -2, "count"); 947 lua_setfield(L, -2, "count");
967 948
968 lua_pushcfunction( L, LG_linda_get); 949 lua_pushcfunction(L, LG_linda_get);
969 lua_pushcclosure( L, LG_linda_protected_call, 1); 950 lua_pushcclosure(L, LG_linda_protected_call, 1);
970 lua_setfield( L, -2, "get"); 951 lua_setfield(L, -2, "get");
971 952
972 lua_pushcfunction( L, LG_linda_cancel); 953 lua_pushcfunction(L, LG_linda_cancel);
973 lua_setfield( L, -2, "cancel"); 954 lua_setfield(L, -2, "cancel");
974 955
975 lua_pushcfunction( L, LG_linda_deep); 956 lua_pushcfunction(L, LG_linda_deep);
976 lua_setfield( L, -2, "deep"); 957 lua_setfield(L, -2, "deep");
977 958
978 lua_pushcfunction( L, LG_linda_dump); 959 lua_pushcfunction(L, LG_linda_dump);
979 lua_pushcclosure( L, LG_linda_protected_call, 1); 960 lua_pushcclosure(L, LG_linda_protected_call, 1);
980 lua_setfield( L, -2, "dump"); 961 lua_setfield(L, -2, "dump");
981 962
982 // some constants 963 // some constants
983 lua_pushliteral( L, BATCH_SENTINEL); 964 BATCH_SENTINEL.push(L);
984 lua_setfield( L, -2, "batched"); 965 lua_setfield(L, -2, "batched");
985 966
986 NIL_SENTINEL.push(L); 967 NIL_SENTINEL.push(L);
987 lua_setfield( L, -2, "null"); 968 lua_setfield(L, -2, "null");
988 969
989 STACK_CHECK( L, 1); 970 STACK_CHECK(L, 1);
990 return nullptr; 971 return nullptr;
991 } 972 }
992 973
@@ -1001,24 +982,26 @@ static void* linda_id( lua_State* L, DeepOp op_)
1001 } 982 }
1002} 983}
1003 984
985// #################################################################################################
986
1004/* 987/*
1005 * ud = lanes.linda( [name[,group]]) 988 * ud = lanes.linda( [name[,group]])
1006 * 989 *
1007 * returns a linda object, or raises an error if creation failed 990 * returns a linda object, or raises an error if creation failed
1008 */ 991 */
1009LUAG_FUNC( linda) 992LUAG_FUNC(linda)
1010{ 993{
1011 int const top = lua_gettop( L); 994 int const top = lua_gettop(L);
1012 luaL_argcheck( L, top <= 2, top, "too many arguments"); 995 luaL_argcheck(L, top <= 2, top, "too many arguments");
1013 if( top == 1) 996 if (top == 1)
1014 { 997 {
1015 int const t = lua_type( L, 1); 998 int const t = lua_type(L, 1);
1016 luaL_argcheck( L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); 999 luaL_argcheck(L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)");
1017 } 1000 }
1018 else if( top == 2) 1001 else if (top == 2)
1019 { 1002 {
1020 luaL_checktype( L, 1, LUA_TSTRING); 1003 luaL_checktype(L, 1, LUA_TSTRING);
1021 luaL_checktype( L, 2, LUA_TNUMBER); 1004 luaL_checktype(L, 2, LUA_TNUMBER);
1022 } 1005 }
1023 return luaG_newdeepuserdata( L, linda_id, 0); 1006 return luaG_newdeepuserdata(L, linda_id, 0);
1024} 1007}
diff --git a/src/uniquekey.h b/src/uniquekey.h
index 777d640..93aaf37 100644
--- a/src/uniquekey.h
+++ b/src/uniquekey.h
@@ -3,6 +3,8 @@
3#include "compat.h" 3#include "compat.h"
4#include "macros_and_utils.h" 4#include "macros_and_utils.h"
5 5
6#include <bit>
7
6class UniqueKey 8class UniqueKey
7{ 9{
8 private: 10 private:
@@ -31,13 +33,11 @@ class UniqueKey
31 33
32 void push(lua_State* const L) const 34 void push(lua_State* const L) const
33 { 35 {
34 // unfortunately, converting a scalar to a pointer must go through a C cast 36 lua_pushlightuserdata(L, std::bit_cast<void*>(m_storage));
35 lua_pushlightuserdata(L, (void*) m_storage);
36 } 37 }
37 bool equals(lua_State* const L, int i) const 38 bool equals(lua_State* const L, int i) const
38 { 39 {
39 // unfortunately, converting a scalar to a pointer must go through a C cast 40 return lua_touserdata(L, i) == std::bit_cast<void*>(m_storage);
40 return lua_touserdata(L, i) == (void*) m_storage;
41 } 41 }
42 void query_registry(lua_State* const L) const 42 void query_registry(lua_State* const L) const
43 { 43 {