From 307fd830eb168005a3ba3d557343284814757eff Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 3 Dec 2024 10:26:47 +0100 Subject: New method linda:restrict() --- src/cancel.cpp | 2 +- src/compat.hpp | 10 ++++ src/keeper.cpp | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- src/keeper.hpp | 14 ++++++ src/lanes.lua | 1 + src/linda.cpp | 104 +++++++++++++++++++++++++++++++---------- 6 files changed, 239 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/cancel.cpp b/src/cancel.cpp index 8fa68d5..31656c4 100644 --- a/src/cancel.cpp +++ b/src/cancel.cpp @@ -164,7 +164,7 @@ LUAG_FUNC(lane_cancel) raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); } lua_remove(L_, 2); // argument is processed, remove it - } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key + } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil lua_remove(L_, 2); // argument is processed, remove it } diff --git a/src/compat.hpp b/src/compat.hpp index f66f703..81f1665 100644 --- a/src/compat.hpp +++ b/src/compat.hpp @@ -272,6 +272,16 @@ LuaType luaG_getmodule(lua_State* L_, std::string_view const& name_); // ################################################################################################# +template +requires std::is_enum_v +[[nodiscard]] +ENUM luaG_optenum(lua_State* const L_, StackIndex const idx_, ENUM const def_) +{ + return static_cast(luaL_optinteger(L_, idx_, static_cast>(def_))); +} + +// ################################################################################################# + inline void luaG_registerlibfuncs(lua_State* L_, luaL_Reg const* funcs_) { // fake externs to make clang happy... diff --git a/src/keeper.cpp b/src/keeper.cpp index 8a99a36..e7b02e6 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp @@ -73,6 +73,7 @@ class KeyUD int first{ 1 }; int count{ 0 }; LindaLimit limit{ -1 }; + LindaRestrict restrict { LindaRestrict::None }; // a fifo full userdata has one uservalue, the table that holds the actual fifo contents [[nodiscard]] @@ -84,6 +85,8 @@ class KeyUD [[nodiscard]] bool changeLimit(LindaLimit limit_); [[nodiscard]] + LindaRestrict changeRestrict(LindaRestrict restrict_); + [[nodiscard]] static KeyUD* Create(KeeperState K_); [[nodiscard]] static KeyUD* GetPtr(KeeperState K_, StackIndex idx_); @@ -114,6 +117,14 @@ bool KeyUD::changeLimit(LindaLimit const limit_) // ################################################################################################# +[[nodiscard]] +LindaRestrict KeyUD::changeRestrict(LindaRestrict const restrict_) +{ + return std::exchange(restrict, restrict_); +} + +// ################################################################################################# + // in: nothing // out: { first = 1, count = 0, limit = -1} KeyUD* KeyUD::Create(KeeperState const K_) @@ -280,7 +291,7 @@ bool KeyUD::reset(KeeperState const K_) LUA_ASSERT(K_, KeyUD::GetPtr(K_, kIdxTop) == this); STACK_CHECK_START_REL(K_, 0); bool const _wasFull{ (limit > 0) && (count >= limit) }; - // empty the KeyUD: replace uservalue with a virgin table, reset counters, but leave limit unchanged! + // empty the KeyUD: replace uservalue with a virgin table, reset counters, but leave limit and restrict unchanged! // if we have an actual limit, use it to preconfigure the table lua_createtable(K_, (limit <= 0) ? 0 : limit.value(), 0); // K_: KeysDB key val... KeyUD {} lua_setiuservalue(K_, StackIndex{ -2 }, kContentsTableIndex); // K_: KeysDB key val... KeyUD @@ -408,7 +419,7 @@ int keepercall_destruct(lua_State* const L_) // ################################################################################################# // in: linda_ud key [count] -// out: bool + at most values +// out: N |kRestrictedChannel int keepercall_get(lua_State* const L_) { KeeperState const _K{ L_ }; @@ -423,7 +434,13 @@ int keepercall_get(lua_State* const L_) lua_remove(_K, 1); // _K: KeyUD KeyUD const* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; if (_key != nullptr) { - _key->peek(_K, _count); // _K: N val... + if (_key->restrict == LindaRestrict::SendReceive) { // can we use set/get? + lua_settop(_K, 0); // _K: + kRestrictedChannel.pushKey(_K); // _K: kRestrictedChannel + return 1; + } else { + _key->peek(_K, _count); // _K: N val... + } } else { // no fifo was ever registered for this key, or it is empty lua_pop(_K, 1); // _K: @@ -492,6 +509,17 @@ int keepercall_receive(lua_State* const L_) lua_rawget(_K, 1); // _K: KeysDB keys... KeyUD KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; if (_key != nullptr) { // it's fine to attempt a read on a key that wasn't yet written to + if (_key->restrict == LindaRestrict::SetGet) { // can we use send/receive? + kRestrictedChannel.pushKey(_K); // _K: KeysDB keys... key[i] kRestrictedChannel + lua_replace(_K, 1); // _K: kRestrictedChannel keys... key[i] + lua_settop(_K, _keyIdx); // _K: kRestrictedChannel keys... key[i] + if (_keyIdx != 2) { + lua_replace(_K, 2); // _K: kRestrictedChannel key[i] keys... + lua_settop(_K, 2); // _K: kRestrictedChannel key[i] + } + lua_insert(_K, 1); // _K: key kRestrictedChannel + return 2; + } int const _popped{ _key->pop(_K, 1, 1) }; // _K: KeysDB keys... val if (_popped > 0) { lua_replace(_K, 1); // _K: val keys... @@ -527,19 +555,89 @@ int keepercall_receive_batched(lua_State* const L_) lua_rawget(_K, 2); // _K: key KeysDB KeyUD lua_remove(_K, 2); // _K: key KeyUD KeyUD* const _key{ KeyUD::GetPtr(_K, kIdxTop) }; - if (_key == nullptr || _key->pop(_K, _min_count, _max_count) == 0) { // _K: [key val...]|crap - // Lua will adjust the stack for us when we return - return 0; + if (!_key) { + return 0; // Lua will adjust the stack for us when we return + } + if (_key->restrict == LindaRestrict::SetGet) { // can we use send/receive? + lua_settop(_K, 1); // _K: key + kRestrictedChannel.pushKey(_K); // _K: key kRestrictedChannel + return 2; + } + if (_key->pop(_K, _min_count, _max_count) == 0) { // _K: [key val...]|crap + return 0; // Lua will adjust the stack for us when we return + } + // return whatever remains on the stack at that point: the key and the values we pulled from the fifo + return lua_gettop(_K); +} + +// ################################################################################################# + +// in: linda key [mode] +// out: mode +int keepercall_restrict(lua_State* const L_) +{ + KeeperState const _K{ L_ }; + STACK_CHECK_START_ABS(_K, lua_gettop(_K)); + // no restriction to set, means we read and return the current restriction instead + bool const _reading{ lua_gettop(_K) == 2 }; + auto _decodeRestrict = [_K, _reading]() { + if (_reading) { + return LindaRestrict::None; + } + std::string_view const _val{ luaG_tostring(_K, StackIndex{ 3 }) }; + if (_val == "set/get") { + return LindaRestrict::SetGet; + } + if (_val == "send/receive") { + return LindaRestrict::SendReceive; + } + return LindaRestrict::None; + }; + auto _encodeRestrict = [](LindaRestrict const val_) { + switch (val_) { + default: + case LindaRestrict::None: + return std::string_view{ "none" }; + case LindaRestrict::SetGet: + return std::string_view{ "set/get" }; + case LindaRestrict::SendReceive: + return std::string_view{ "send/receive" }; + } + }; + LindaRestrict const _rstrct{ _decodeRestrict() }; // if we read nil because the argument is absent + lua_settop(_K, 2); // _K: linda key + PushKeysDB(_K, StackIndex{ 1 }); // _K: linda key KeysDB + lua_replace(_K, 1); // _K: KeysDB key + lua_pushvalue(_K, -1); // _K: KeysDB key key + lua_rawget(_K, -3); // _K: KeysDB key KeyUD|nil + KeyUD* _key{ KeyUD::GetPtr(_K, kIdxTop) }; + if (_reading) { + // remove any clutter on the stack + lua_settop(_K, 0); // _K: + auto const _prevRstrct{ _key ? _key->restrict : LindaRestrict::None }; + // return a single value: the restrict mode of the key + luaG_pushstring(_K, _encodeRestrict(_prevRstrct)); // _K: _previous } else { - // return whatever remains on the stack at that point: the key and the values we pulled from the fifo - return lua_gettop(_K); + if (_key == nullptr) { // _K: KeysDB key nil + lua_pop(_K, 1); // _K: KeysDB key + _key = KeyUD::Create(_K); // _K: KeysDB key KeyUD + lua_rawset(_K, -3); // _K: KeysDB + } + // remove any clutter on the stack + lua_settop(_K, 0); // _K: + // return true if we decide that blocked threads waiting to write on that key should be awakened + // this is the case if we detect the key was full but it is no longer the case + LindaRestrict const _previous{ _key->changeRestrict(_rstrct) }; + luaG_pushstring(_K, _encodeRestrict(_previous)); // _K: _previous } + STACK_CHECK(_K, 1); + return 1; } // ################################################################################################# // in: linda, key, ... -// out: true|false +// out: true|false|kRestrictedChannel int keepercall_send(lua_State* const L_) { KeeperState const _K{ L_ }; @@ -561,7 +659,11 @@ int keepercall_send(lua_State* const L_) lua_pop(_K, 1); // _K: linda KeyUD val... STACK_CHECK(_K, 0); KeyUD* const _key{ KeyUD::GetPtr(_K, StackIndex{ 2 }) }; - if (_key && _key->push(_K, _n, true)) { // not enough room? + if (_key->restrict == LindaRestrict::SetGet) { // can we use send/receive? + lua_settop(_K, 0); // _K: + kRestrictedChannel.pushKey(_K); // _K: kRestrictedChannel + } + else if (_key->push(_K, _n, true)) { // not enough room? lua_settop(_K, 0); // _K: lua_pushboolean(_K, 1); // _K: true } else { @@ -575,7 +677,7 @@ int keepercall_send(lua_State* const L_) // ################################################################################################# // in: linda key [val...] -// out: true if the linda was full but it's no longer the case, else false +// out: true if the linda was full but it's no longer the case, else false, or kRestrictedChannel if the key is restricted int keepercall_set(lua_State* const L_) { KeeperState const _K{ L_ }; @@ -588,11 +690,16 @@ int keepercall_set(lua_State* const L_) lua_pushvalue(_K, 2); // _K: KeysDB key val... key lua_rawget(_K, 1); // _K: KeysDB key val KeyUD|nil KeyUD* _key{ KeyUD::GetPtr(_K, kIdxTop) }; + if (_key && _key->restrict == LindaRestrict::SendReceive) { // can we use send/receive? + lua_settop(_K, 0); // _K: + kRestrictedChannel.pushKey(_K); // _K: kRestrictedChannel + return 1; + } if (lua_gettop(_K) == 3) { // no value to set // _K: KeysDB key KeyUD|nil // empty the KeyUD for the specified key: replace uservalue with a virgin table, reset counters, but leave limit unchanged! if (_key != nullptr) { // might be nullptr if we set a nonexistent key to nil // _K: KeysDB key KeyUD - if (_key->limit < 0) { // KeyUD limit value is the default (unlimited): we can totally remove it + if (_key->limit < 0 && _key->restrict == LindaRestrict::None) { // KeyUD limit value and restrict mode are the default (unlimited/none): we can totally remove it lua_pop(_K, 1); // _K: KeysDB key lua_pushnil(_K); // _K: KeysDB key nil lua_rawset(_K, -3); // _K: KeysDB @@ -777,6 +884,20 @@ int Keeper::PushLindaStorage(Linda& linda_, DestState const L_) } STACK_CHECK(L_, 5); lua_setfield(L_, -3, "limit"); // _K: KeysDB key L_: out key keyout fifo + // keyout.restrict + switch (_key->restrict) { + case LindaRestrict::None: + luaG_pushstring(L_, "none"); // _K: KeysDB key L_: out key keyout fifo restrict + break; + case LindaRestrict::SetGet: + luaG_pushstring(L_, "set/get"); // _K: KeysDB key L_: out key keyout fifo restrict + break; + case LindaRestrict::SendReceive: + luaG_pushstring(L_, "send/receive"); // _K: KeysDB key L_: out key keyout fifo restrict + break; + } + STACK_CHECK(L_, 5); + lua_setfield(L_, -3, "restrict"); // _K: KeysDB key L_: out key keyout fifo // keyout.fifo lua_setfield(L_, -2, "fifo"); // _K: KeysDB key L_: out key keyout // out[key] = keyout diff --git a/src/keeper.hpp b/src/keeper.hpp index b77f1a9..e2ad445 100644 --- a/src/keeper.hpp +++ b/src/keeper.hpp @@ -13,6 +13,15 @@ DECLARE_UNIQUE_TYPE(KeeperIndex, int); // ################################################################################################# +enum class LindaRestrict +{ + None, + SetGet, + SendReceive +}; + +// ################################################################################################# + struct Keeper { std::mutex mutex; @@ -79,6 +88,9 @@ DECLARE_UNIQUE_TYPE(KeeperCallResult, std::optional); // xxh64 of string "kNilSentinel" generated at https://www.pelock.com/products/hash-calculator static constexpr UniqueKey kNilSentinel{ 0xC457D4EDDB05B5E4ull, "lanes.null" }; +// xxh64 of string "kRestrictedChannel" generated at https://www.pelock.com/products/hash-calculator +static constexpr UniqueKey kRestrictedChannel{ 0x4C8B879ECDE110F7ull }; + using keeper_api_t = lua_CFunction; #define KEEPER_API(_op) keepercall_##_op @@ -96,6 +108,8 @@ int keepercall_receive(lua_State* L_); [[nodiscard]] int keepercall_receive_batched(lua_State* L_); [[nodiscard]] +int keepercall_restrict(lua_State* L_); +[[nodiscard]] int keepercall_send(lua_State* L_); [[nodiscard]] int keepercall_set(lua_State* L_); diff --git a/src/lanes.lua b/src/lanes.lua index dfa959d..8d8f25d 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -448,6 +448,7 @@ local configure_timers = function() -- Timer lane; initialize only on the first 'require "lanes"' instance (which naturally has 'table' always declared) local first_time_key = "first time" + timerLinda:restrict(first_time_key, "set/get") local _, _first_time_val = timerLinda:get(first_time_key) local first_time = (_first_time_val == nil) timerLinda:set(first_time_key, true) 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 { case LuaType::USERDATA: if (!DeepFactory::IsDeepUserdata(L_, _i)) { - raise_luaL_error(L_, "argument #%d: can't use non-deep userdata as a key", _i); + raise_luaL_error(L_, "argument #%d: can't use non-deep userdata as a slot", _i); } break; @@ -66,7 +66,7 @@ namespace { static constexpr std::array, 3> kKeysToCheck{ kLindaBatched, kCancelError, kNilSentinel }; for (UniqueKey const& _key : kKeysToCheck) { if (_key.equals(L_, _i)) { - raise_luaL_error(L_, "argument #%d: can't use %s as a key", _i, _key.debugName.data()); + raise_luaL_error(L_, "argument #%d: can't use %s as a slot", _i, _key.debugName.data()); break; } } @@ -74,7 +74,7 @@ namespace { break; default: - raise_luaL_error(L_, "argument #%d: invalid key type (not a boolean, string, number or light userdata)", _i); + raise_luaL_error(L_, "argument #%d: invalid slot type (not a boolean, string, number or light userdata)", _i); } } STACK_CHECK(L_, 0); @@ -416,7 +416,7 @@ static LUAG_FUNC(linda_index) // ################################################################################################# /* - * [val] = linda_count( linda_ud, [key [, ...]]) + * [val] = linda_count( linda_ud, [slot [, ...]]) * * Get a count of the pending elements in the specified keys */ @@ -430,7 +430,7 @@ LUAG_FUNC(linda_count) Keeper* const _keeper{ _linda->whichKeeper() }; KeeperCallResult const _pushed{ keeper_call(_keeper->K, KEEPER_API(count), L_, _linda, StackIndex{ 2 }) }; - return OptionalValue(_pushed, L_, "tried to count an invalid key"); + return OptionalValue(_pushed, L_, "Tried to count an invalid slot"); } }; return Linda::ProtectedCall(L_, _count); @@ -487,13 +487,16 @@ LUAG_FUNC(linda_get) lua_Integer const _count{ luaL_optinteger(L_, 3, 1) }; luaL_argcheck(L_, _count >= 1, 3, "count should be >= 1"); luaL_argcheck(L_, lua_gettop(L_) <= 3, 4, "too many arguments"); - // make sure the key is of a valid type (throws an error if not the case) + // make sure the slot is of a valid type (throws an error if not the case) CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); KeeperCallResult _pushed; if (_linda->cancelStatus == Linda::Active) { Keeper* const _keeper{ _linda->whichKeeper() }; _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, StackIndex{ 2 }); + if (_pushed.has_value() && kRestrictedChannel.equals(L_, kIdxTop)) { + raise_luaL_error(L_, "Key is restricted"); + } } else { // linda is cancelled // do nothing and return nil,lanes.cancel_error lua_pushnil(L_); @@ -511,7 +514,7 @@ LUAG_FUNC(linda_get) /* * [bool]|nil,cancel_error = linda:limit(key_num|str|bool|lightuserdata, [int]) - * "unlimited"|number = linda:limit(key) + * "unlimited"|number = linda:limit(slot) * * Read or set limit to 1 Linda keys. * 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) static constexpr lua_CFunction _limit{ +[](lua_State* const L_) { Linda* const _linda{ ToLinda(L_, StackIndex{ 1 }) }; - // make sure we got 2 or 3 arguments: the linda, a key and optionally a limit + // make sure we got 2 or 3 arguments: the linda, a slot and optionally a limit int const _nargs{ lua_gettop(L_) }; luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); // make sure we got a numeric limit, or "unlimited", (or nothing) @@ -531,7 +534,7 @@ LUAG_FUNC(linda_limit) if (_val < 0) { raise_luaL_argerror(L_, StackIndex{ 3 }, "limit must be >= 0"); } - // make sure the key is of a valid type + // make sure the slot is of a valid type CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); KeeperCallResult _pushed; @@ -539,8 +542,8 @@ LUAG_FUNC(linda_limit) if (_unlimited) { LUA_ASSERT(L_, lua_gettop(L_) == 3 && luaG_tostring(L_, StackIndex{ 3 }) == "unlimited"); // inside the Keeper, unlimited is signified with a -1 limit (can't use nil because of nil kNilSentinel conversions!) - lua_pop(L_, 1); // L_: linda key - lua_pushinteger(L_, -1); // L_: linda key nil + lua_pop(L_, 1); // L_: linda slot + lua_pushinteger(L_, -1); // L_: linda slot nil } Keeper* const _keeper{ _linda->whichKeeper() }; _pushed = keeper_call(_keeper->K, KEEPER_API(limit), L_, _linda, StackIndex{ 2 }); @@ -572,12 +575,12 @@ LUAG_FUNC(linda_limit) /* * 2 modes of operation - * [val, key]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) - * Consumes a single value from the Linda, in any key. - * Returns: received value (which is consumed from the slot), and the key which had it + * [val, slot]= linda:receive([timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) + * Consumes a single value from the Linda, in any slot. + * Returns: received value (which is consumed from the slot), and the slot which had it * [val1, ... valCOUNT]= linda_receive( linda_ud, [timeout_secs_num=-1], linda.batched, key_num|str|bool|lightuserdata, min_COUNT[, max_COUNT]) - * Consumes between min_COUNT and max_COUNT values from the linda, from a single key. + * Consumes between min_COUNT and max_COUNT values from the linda, from a single slot. * returns the actual consumed values, or nil if there weren't enough values to consume */ LUAG_FUNC(linda_receive) @@ -585,7 +588,7 @@ LUAG_FUNC(linda_receive) static constexpr lua_CFunction _receive{ +[](lua_State* const L_) { Linda* const _linda{ ToLinda(L_, StackIndex{ 1 }) }; - StackIndex _key_i{ 2 }; // index of first key, if timeout not there + StackIndex _key_i{ 2 }; // index of first slot, if timeout not there std::chrono::time_point _until{ std::chrono::time_point::max() }; 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) raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); } ++_key_i; - } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key + } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the slot ++_key_i; } @@ -616,7 +619,7 @@ LUAG_FUNC(linda_receive) raise_luaL_argerror(L_, StackIndex{ _key_i + 1 }, "bad min count"); } _expected_pushed_max = (int) luaL_optinteger(L_, _key_i + 2, _expected_pushed_min); - // don't forget to count the key in addition to the values + // don't forget to count the slot in addition to the values ++_expected_pushed_min; ++_expected_pushed_max; if (_expected_pushed_min > _expected_pushed_max) { @@ -627,7 +630,7 @@ LUAG_FUNC(linda_receive) CheckKeyTypes(L_, _key_i, StackIndex{ lua_gettop(L_) }); // receive a single value, checking multiple slots _selected_keeper_receive = KEEPER_API(receive); - // we expect a single (value, key) pair of returned values + // we expect a single (value, slot) pair of returned values _expected_pushed_min = _expected_pushed_max = 2; } @@ -660,6 +663,9 @@ LUAG_FUNC(linda_receive) } if (_pushed.value() > 0) { LUA_ASSERT(L_, _pushed.value() >= _expected_pushed_min && _pushed.value() <= _expected_pushed_max); + if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) { + raise_luaL_error(L_, "Key is restricted"); + } _linda->readHappened.notify_all(); break; } @@ -729,6 +735,49 @@ LUAG_FUNC(linda_receive) // ################################################################################################# +/* + * "string" = linda:restrict(key_num|str|bool|lightuserdata, [string]) + * "string" = linda:restrict(slot) + * + * Read or set restrict mode to 1 Linda slot. + */ +LUAG_FUNC(linda_restrict) +{ + static constexpr lua_CFunction _rstrct{ + +[](lua_State* const L_) { + Linda* const _linda{ ToLinda(L_, StackIndex{ 1 }) }; + // make sure we got 2 or 3 arguments: the linda, a slot and optionally a restrict mode + int const _nargs{ lua_gettop(L_) }; + luaL_argcheck(L_, _nargs == 2 || _nargs == 3, 2, "wrong number of arguments"); + // make sure we got a known restrict mode, (or nothing) + std::string_view const _mode{ luaG_tostring(L_, StackIndex{ 3 }) }; + if (!_mode.empty() && (_mode != "none" && _mode != "set/get" && _mode != "send/receive")) { + raise_luaL_argerror(L_, StackIndex{ 3 }, "unknown restrict mode"); + } + // make sure the slot is of a valid type + CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); + + KeeperCallResult _pushed; + if (_linda->cancelStatus == Linda::Active) { + Keeper* const _keeper{ _linda->whichKeeper() }; + _pushed = keeper_call(_keeper->K, KEEPER_API(restrict), L_, _linda, StackIndex{ 2 }); + // we should get a single return value: the string describing the previous restrict mode + LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, kIdxTop) == LuaType::STRING); + } else { // linda is cancelled + // do nothing and return nil,lanes.cancel_error + lua_pushnil(L_); + kCancelError.pushKey(L_); + _pushed.emplace(2); + } + // propagate returned values + return _pushed.value(); + } + }; + return Linda::ProtectedCall(L_, _rstrct); +} + +// ################################################################################################# + /* * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) * @@ -743,7 +792,7 @@ LUAG_FUNC(linda_send) static constexpr lua_CFunction _send{ +[](lua_State* const L_) { Linda* const _linda{ ToLinda(L_, StackIndex{ 1 }) }; - StackIndex _key_i{ 2 }; // index of first key, if timeout not there + StackIndex _key_i{ 2 }; // index of first slot, if timeout not there std::chrono::time_point _until{ std::chrono::time_point::max() }; 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) raise_luaL_argerror(L_, StackIndex{ 2 }, "duration cannot be < 0"); } ++_key_i; - } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key + } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the slot ++_key_i; } - // make sure the key is of a valid type + // make sure the slot is of a valid type CheckKeyTypes(L_, _key_i, _key_i); STACK_GROW(L_, 1); @@ -799,6 +848,9 @@ LUAG_FUNC(linda_send) } LUA_ASSERT(L_, _pushed.value() == 1); + if (kRestrictedChannel.equals(L_, StackIndex{ kIdxTop })) { + raise_luaL_error(L_, "Key is restricted"); + } _ret = lua_toboolean(L_, -1) ? true : false; lua_pop(L_, 1); @@ -884,7 +936,7 @@ LUAG_FUNC(linda_set) +[](lua_State* const L_) { Linda* const _linda{ ToLinda(L_, StackIndex{ 1 }) }; bool const _has_data{ lua_gettop(L_) > 2 }; - // make sure the key is of a valid type (throws an error if not the case) + // make sure the slot is of a valid type (throws an error if not the case) CheckKeyTypes(L_, StackIndex{ 2 }, StackIndex{ 2 }); KeeperCallResult _pushed; @@ -892,6 +944,9 @@ LUAG_FUNC(linda_set) Keeper* const _keeper{ _linda->whichKeeper() }; _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, StackIndex{ 2 }); if (_pushed.has_value()) { // no error? + if (kRestrictedChannel.equals(L_, kIdxTop)) { + raise_luaL_error(L_, "Key is restricted"); + } LUA_ASSERT(L_, _pushed.value() == 2 && luaG_type(L_, kIdxTop) == LuaType::STRING && luaG_type(L_, StackIndex{ -2 }) == LuaType::BOOLEAN); if (_has_data) { @@ -899,7 +954,7 @@ LUAG_FUNC(linda_set) _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area } if (lua_toboolean(L_, -2)) { - // the key was full, but it is no longer the case, tell writers they should wake + // the slot was full, but it is no longer the case, tell writers they should wake _linda->readHappened.notify_all(); // To be done from within the 'K' locking area } } @@ -992,6 +1047,7 @@ namespace { { "get", LG_linda_get }, { "limit", LG_linda_limit }, { "receive", LG_linda_receive }, + { "restrict", LG_linda_restrict }, { "send", LG_linda_send }, { "set", LG_linda_set }, { "wake", LG_linda_wake }, -- cgit v1.2.3-55-g6feb