aboutsummaryrefslogtreecommitdiff
path: root/src/linda.cpp
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-12-03 10:26:47 +0100
committerBenoit Germain <benoit.germain@ubisoft.com>2024-12-03 10:26:47 +0100
commit307fd830eb168005a3ba3d557343284814757eff (patch)
treeeb14512492d58e33585bc5df3f3d9b23d7308934 /src/linda.cpp
parentca7657e24549acb8a2dd45fa81c309b5bf9f61ee (diff)
downloadlanes-307fd830eb168005a3ba3d557343284814757eff.tar.gz
lanes-307fd830eb168005a3ba3d557343284814757eff.tar.bz2
lanes-307fd830eb168005a3ba3d557343284814757eff.zip
New method linda:restrict()
Diffstat (limited to 'src/linda.cpp')
-rw-r--r--src/linda.cpp104
1 files changed, 80 insertions, 24 deletions
diff --git a/src/linda.cpp b/src/linda.cpp
index 43d2a91..6ebbc1f 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -57,7 +57,7 @@ namespace {
57 57
58 case LuaType::USERDATA: 58 case LuaType::USERDATA:
59 if (!DeepFactory::IsDeepUserdata(L_, _i)) { 59 if (!DeepFactory::IsDeepUserdata(L_, _i)) {
60 raise_luaL_error(L_, "argument #%d: can't use non-deep userdata as a key", _i); 60 raise_luaL_error(L_, "argument #%d: can't use non-deep userdata as a slot", _i);
61 } 61 }
62 break; 62 break;
63 63
@@ -66,7 +66,7 @@ namespace {
66 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; 66 static constexpr std::array<std::reference_wrapper<UniqueKey const>, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel };
67 for (UniqueKey const& _key : kKeysToCheck) { 67 for (UniqueKey const& _key : kKeysToCheck) {
68 if (_key.equals(L_, _i)) { 68 if (_key.equals(L_, _i)) {
69 raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, _key.debugName.data()); 69 raise_luaL_error(L_, "argument #%d: can't use %s as a slot", _i, _key.debugName.data());
70 break; 70 break;
71 } 71 }
72 } 72 }
@@ -74,7 +74,7 @@ namespace {
74 break; 74 break;
75 75
76 default: 76 default:
77 raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i); 77 raise_luaL_error(L_, "argument #%d: invalid slot type (not a boolean, string, number or light userdata)", _i);
78 } 78 }
79 } 79 }
80 STACK_CHECK(L_, 0); 80 STACK_CHECK(L_, 0);
@@ -416,7 +416,7 @@ static LUAG_FUNC(linda_index)
416// ################################################################################################# 416// #################################################################################################
417 417
418/* 418/*
419 * [val] = linda_count( linda_ud, [key [, ...]]) 419 * [val] = linda_count( linda_ud, [slot [, ...]])
420 * 420 *
421 * Get a count of the pending elements in the specified keys 421 * Get a count of the pending elements in the specified keys
422 */ 422 */
@@ -430,7 +430,7 @@ LUAG_FUNC(linda_count)
430 430
431 Keeper* const _keeper{ _linda->whichKeeper() }; 431 Keeper* const _keeper{ _linda->whichKeeper() };
432 KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(count), L_, _linda, StackIndex{ 2 }) }; 432 KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(count), L_, _linda, StackIndex{ 2 }) };
433 return OptionalValue(_pushed, L_, "tried to count an invalid key"); 433 return OptionalValue(_pushed, L_, "Tried to count an invalid slot");
434 } 434 }
435 }; 435 };
436 return Linda::ProtectedCall(L_, _count); 436 return Linda::ProtectedCall(L_, _count);
@@ -487,13 +487,16 @@ LUAG_FUNC(linda_get)
487 lua_Integer const _count{ luaL_optinteger(L_, 3, 1) }; 487 lua_Integer const _count{ luaL_optinteger(L_, 3, 1) };
488 luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1"); 488 luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1");
489 luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments"); 489 luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments");
490 // make sure the key is of a valid type (throws an error if not the case) 490 // make sure the slot is of a valid type (throws an error if not the case)
491 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); 491 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
492 492
493 KeeperCallResult _pushed; 493 KeeperCallResult _pushed;
494 if (_linda->cancelStatus == Linda::Active) { 494 if (_linda->cancelStatus == Linda::Active) {
495 Keeper* const _keeper{ _linda->whichKeeper() }; 495 Keeper* const _keeper{ _linda->whichKeeper() };
496 _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, StackIndex{ 2 }); 496 _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, StackIndex{ 2 });
497 if (_pushed.has_value() && kRestrictedChannel.equals(L_, kIdxTop)) {
498 raise_luaL_error(L_, "Key is restricted");
499 }
497 } else { // linda is cancelled 500 } else { // linda is cancelled
498 // do nothing and return nil,lanes.cancel_error 501 // do nothing and return nil,lanes.cancel_error
499 lua_pushnil(L_); 502 lua_pushnil(L_);
@@ -511,7 +514,7 @@ LUAG_FUNC(linda_get)
511 514
512/* 515/*
513 * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int]) 516 * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int])
514 * "unlimited"|number = linda:limit(key) 517 * "unlimited"|number = linda:limit(slot)
515 * 518 *
516 * Read or set limit to 1 Linda keys. 519 * Read or set limit to 1 Linda keys.
517 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so 520 * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so
@@ -522,7 +525,7 @@ LUAG_FUNC(linda_limit)
522 static constexpr lua_CFunction _limit{ 525 static constexpr lua_CFunction _limit{
523 +[](lua_State* const L_) { 526 +[](lua_State* const L_) {
524 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 527 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
525 // make sure we got 2 or 3 arguments: the linda, a key and optionally a limit 528 // make sure we got 2 or 3 arguments: the linda, a slot and optionally a limit
526 int const _nargs{ lua_gettop(L_) }; 529 int const _nargs{ lua_gettop(L_) };
527 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); 530 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
528 // make sure we got a numeric limit, or "unlimited", (or nothing) 531 // make sure we got a numeric limit, or "unlimited", (or nothing)
@@ -531,7 +534,7 @@ LUAG_FUNC(linda_limit)
531 if (_val < 0) { 534 if (_val < 0) {
532 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0"); 535 raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0");
533 } 536 }
534 // make sure the key is of a valid type 537 // make sure the slot is of a valid type
535 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); 538 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
536 539
537 KeeperCallResult _pushed; 540 KeeperCallResult _pushed;
@@ -539,8 +542,8 @@ LUAG_FUNC(linda_limit)
539 if (_unlimited) { 542 if (_unlimited) {
540 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited"); 543 LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited");
541 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!) 544 // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!)
542 lua_pop(L_, 1); // L_: linda key 545 lua_pop(L_, 1); // L_: linda slot
543 lua_pushinteger(L_, -1); // L_: linda key nil 546 lua_pushinteger(L_, -1); // L_: linda slot nil
544 } 547 }
545 Keeper* const _keeper{ _linda->whichKeeper() }; 548 Keeper* const _keeper{ _linda->whichKeeper() };
546 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 }); 549 _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 });
@@ -572,12 +575,12 @@ LUAG_FUNC(linda_limit)
572 575
573/* 576/*
574 * 2 modes of operation 577 * 2 modes of operation
575 * [val, key]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) 578 * [val, slot]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] )
576 * Consumes a single value from the Linda, in any key. 579 * Consumes a single value from the Linda, in any slot.
577 * Returns: received value (which is consumed from the slot), and the key which had it 580 * Returns: received value (which is consumed from the slot), and the slot which had it
578 581
579 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) 582 * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT])
580 * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. 583 * Consumes between min_COUNT and max_COUNT values from the linda, from a single slot.
581 * returns the actual consumed values, or nil if there weren't enough values to consume 584 * returns the actual consumed values, or nil if there weren't enough values to consume
582 */ 585 */
583LUAG_FUNC(linda_receive) 586LUAG_FUNC(linda_receive)
@@ -585,7 +588,7 @@ LUAG_FUNC(linda_receive)
585 static constexpr lua_CFunction _receive{ 588 static constexpr lua_CFunction _receive{
586 +[](lua_State* const L_) { 589 +[](lua_State* const L_) {
587 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 590 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
588 StackIndex _key_i{ 2 }; // index of first key, if timeout not there 591 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there
589 592
590 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 593 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
591 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 594 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
@@ -596,7 +599,7 @@ LUAG_FUNC(linda_receive)
596 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); 599 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0");
597 } 600 }
598 ++_key_i; 601 ++_key_i;
599 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key 602 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the slot
600 ++_key_i; 603 ++_key_i;
601 } 604 }
602 605
@@ -616,7 +619,7 @@ LUAG_FUNC(linda_receive)
616 raise_luaL_argerror(L_, StackIndex{ _key_i + 1 }, "bad min count"); 619 raise_luaL_argerror(L_, StackIndex{ _key_i + 1 }, "bad min count");
617 } 620 }
618 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min); 621 _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min);
619 // don't forget to count the key in addition to the values 622 // don't forget to count the slot in addition to the values
620 ++_expected_pushed_min; 623 ++_expected_pushed_min;
621 ++_expected_pushed_max; 624 ++_expected_pushed_max;
622 if (_expected_pushed_min > _expected_pushed_max) { 625 if (_expected_pushed_min > _expected_pushed_max) {
@@ -627,7 +630,7 @@ LUAG_FUNC(linda_receive)
627 CheckKeyTypes(L_, _key_i, StackIndex{ lua_gettop(L_) }); 630 CheckKeyTypes(L_, _key_i, StackIndex{ lua_gettop(L_) });
628 // receive a single value, checking multiple slots 631 // receive a single value, checking multiple slots
629 _selected_keeper_receive = KEEPER_API(receive); 632 _selected_keeper_receive = KEEPER_API(receive);
630 // we expect a single (value, key) pair of returned values 633 // we expect a single (value, slot) pair of returned values
631 _expected_pushed_min = _expected_pushed_max = 2; 634 _expected_pushed_min = _expected_pushed_max = 2;
632 } 635 }
633 636
@@ -660,6 +663,9 @@ LUAG_FUNC(linda_receive)
660 } 663 }
661 if (_pushed.value() > 0) { 664 if (_pushed.value() > 0) {
662 LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max); 665 LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max);
666 if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) {
667 raise_luaL_error(L_, "Key is restricted");
668 }
663 _linda->readHappened.notify_all(); 669 _linda->readHappened.notify_all();
664 break; 670 break;
665 } 671 }
@@ -730,6 +736,49 @@ LUAG_FUNC(linda_receive)
730// ################################################################################################# 736// #################################################################################################
731 737
732/* 738/*
739 * "string" = linda:restrict(key_num|str|bool|lightuserdata, [string])
740 * "string" = linda:restrict(slot)
741 *
742 * Read or set restrict mode to 1 Linda slot.
743 */
744LUAG_FUNC(linda_restrict)
745{
746 static constexpr lua_CFunction _rstrct{
747 +[](lua_State* const L_) {
748 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
749 // make sure we got 2 or 3 arguments: the linda, a slot and optionally a restrict mode
750 int const _nargs{ lua_gettop(L_) };
751 luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments");
752 // make sure we got a known restrict mode, (or nothing)
753 std::string_view const _mode{ luaG_tostring(L_, StackIndex{ 3 }) };
754 if (!_mode.empty() && (_mode != "none" && _mode != "set/get" && _mode != "send/receive")) {
755 raise_luaL_argerror(L_, StackIndex{ 3 }, "unknown restrict mode");
756 }
757 // make sure the slot is of a valid type
758 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
759
760 KeeperCallResult _pushed;
761 if (_linda->cancelStatus == Linda::Active) {
762 Keeper* const _keeper{ _linda->whichKeeper() };
763 _pushed = keeper_call(_keeper->K, KEEPER_API(restrict), L_, _linda, StackIndex{ 2 });
764 // we should get a single return value: the string describing the previous restrict mode
765 LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, kIdxTop) == LuaType::STRING);
766 } else { // linda is cancelled
767 // do nothing and return nil,lanes.cancel_error
768 lua_pushnil(L_);
769 kCancelError.pushKey(L_);
770 _pushed.emplace(2);
771 }
772 // propagate returned values
773 return _pushed.value();
774 }
775 };
776 return Linda::ProtectedCall(L_, _rstrct);
777}
778
779// #################################################################################################
780
781/*
733 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) 782 * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...)
734 * 783 *
735 * Send one or more values to a Linda. If there is a limit, all values must fit. 784 * Send one or more values to a Linda. If there is a limit, all values must fit.
@@ -743,7 +792,7 @@ LUAG_FUNC(linda_send)
743 static constexpr lua_CFunction _send{ 792 static constexpr lua_CFunction _send{
744 +[](lua_State* const L_) { 793 +[](lua_State* const L_) {
745 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 794 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
746 StackIndex _key_i{ 2 }; // index of first key, if timeout not there 795 StackIndex _key_i{ 2 }; // index of first slot, if timeout not there
747 796
748 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() }; 797 std::chrono::time_point<std::chrono::steady_clock> _until{ std::chrono::time_point<std::chrono::steady_clock>::max() };
749 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion 798 if (luaG_type(L_, StackIndex{ 2 }) == LuaType::NUMBER) { // we don't want to use lua_isnumber() because of autocoercion
@@ -754,11 +803,11 @@ LUAG_FUNC(linda_send)
754 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); 803 raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0");
755 } 804 }
756 ++_key_i; 805 ++_key_i;
757 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key 806 } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the slot
758 ++_key_i; 807 ++_key_i;
759 } 808 }
760 809
761 // make sure the key is of a valid type 810 // make sure the slot is of a valid type
762 CheckKeyTypes(L_, _key_i, _key_i); 811 CheckKeyTypes(L_, _key_i, _key_i);
763 812
764 STACK_GROW(L_, 1); 813 STACK_GROW(L_, 1);
@@ -799,6 +848,9 @@ LUAG_FUNC(linda_send)
799 } 848 }
800 LUA_ASSERT(L_, _pushed.value() == 1); 849 LUA_ASSERT(L_, _pushed.value() == 1);
801 850
851 if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) {
852 raise_luaL_error(L_, "Key is restricted");
853 }
802 _ret = lua_toboolean(L_, -1) ? true : false; 854 _ret = lua_toboolean(L_, -1) ? true : false;
803 lua_pop(L_, 1); 855 lua_pop(L_, 1);
804 856
@@ -884,7 +936,7 @@ LUAG_FUNC(linda_set)
884 +[](lua_State* const L_) { 936 +[](lua_State* const L_) {
885 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) }; 937 Linda* const _linda{ ToLinda<false>(L_, StackIndex{ 1 }) };
886 bool const _has_data{ lua_gettop(L_) > 2 }; 938 bool const _has_data{ lua_gettop(L_) > 2 };
887 // make sure the key is of a valid type (throws an error if not the case) 939 // make sure the slot is of a valid type (throws an error if not the case)
888 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); 940 CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 });
889 941
890 KeeperCallResult _pushed; 942 KeeperCallResult _pushed;
@@ -892,6 +944,9 @@ LUAG_FUNC(linda_set)
892 Keeper* const _keeper{ _linda->whichKeeper() }; 944 Keeper* const _keeper{ _linda->whichKeeper() };
893 _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, StackIndex{ 2 }); 945 _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, StackIndex{ 2 });
894 if (_pushed.has_value()) { // no error? 946 if (_pushed.has_value()) { // no error?
947 if (kRestrictedChannel.equals(L_, kIdxTop)) {
948 raise_luaL_error(L_, "Key is restricted");
949 }
895 LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, kIdxTop) == LuaType::STRING && luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); 950 LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, kIdxTop) == LuaType::STRING && luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN);
896 951
897 if (_has_data) { 952 if (_has_data) {
@@ -899,7 +954,7 @@ LUAG_FUNC(linda_set)
899 _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area 954 _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area
900 } 955 }
901 if (lua_toboolean(L_, -2)) { 956 if (lua_toboolean(L_, -2)) {
902 // the key was full, but it is no longer the case, tell writers they should wake 957 // the slot was full, but it is no longer the case, tell writers they should wake
903 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area 958 _linda->readHappened.notify_all(); // To be done from within the 'K' locking area
904 } 959 }
905 } 960 }
@@ -992,6 +1047,7 @@ namespace {
992 { "get", LG_linda_get }, 1047 { "get", LG_linda_get },
993 { "limit", LG_linda_limit }, 1048 { "limit", LG_linda_limit },
994 { "receive", LG_linda_receive }, 1049 { "receive", LG_linda_receive },
1050 { "restrict", LG_linda_restrict },
995 { "send", LG_linda_send }, 1051 { "send", LG_linda_send },
996 { "set", LG_linda_set }, 1052 { "set", LG_linda_set },
997 { "wake", LG_linda_wake }, 1053 { "wake", LG_linda_wake },