From 4d0d86f197eda7215a259286d8791efe27df8bde Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 20 May 2024 12:05:28 +0200 Subject: Alpha-sort Linda's Lua API --- src/linda.cpp | 644 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 323 insertions(+), 321 deletions(-) (limited to 'src') diff --git a/src/linda.cpp b/src/linda.cpp index 8ec167d..01b089e 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -105,6 +105,7 @@ template } // ################################################################################################# +// #################################### Linda implementation ####################################### // ################################################################################################# // Any hashing will do that maps pointers to [0..Universe::nb_keepers[ consistently. @@ -205,133 +206,192 @@ void Linda::setName(char const* name_, size_t len_) // ################################################################################################# /* - * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) - * - * Send one or more values to a Linda. If there is a limit, all values must fit. + * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") * - * Returns: 'true' if the value was queued - * 'false' for timeout (only happens when the queue size is limited) - * nil, kCancelError if cancelled + * Signal linda so that waiting threads wake up as if their own lane was cancelled */ -LUAG_FUNC(linda_send) +LUAG_FUNC(linda_cancel) { - auto _send = [](lua_State* L_) { - Linda* const _linda{ ToLinda(L_, 1) }; - int _key_i{ 2 }; // index of first key, if timeout not there + Linda* const _linda{ ToLinda(L_, 1) }; + char const* _who{ luaL_optstring(L_, 2, "both") }; + // make sure we got 3 arguments: the linda, a key and a limit + luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); - std::chrono::time_point _until{ std::chrono::time_point::max() }; - if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion - lua_Duration const _duration{ lua_tonumber(L_, 2) }; - if (_duration.count() >= 0.0) { - _until = std::chrono::steady_clock::now() + std::chrono::duration_cast(_duration); - } else { - raise_luaL_argerror(L_, 2, "duration cannot be < 0"); - } - ++_key_i; - } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key - ++_key_i; - } + _linda->cancelRequest = CancelRequest::Soft; + if (strcmp(_who, "both") == 0) { // tell everyone writers to wake up + _linda->writeHappened.notify_all(); + _linda->readHappened.notify_all(); + } else if (strcmp(_who, "none") == 0) { // reset flag + _linda->cancelRequest = CancelRequest::None; + } else if (strcmp(_who, "read") == 0) { // tell blocked readers to wake up + _linda->writeHappened.notify_all(); + } else if (strcmp(_who, "write") == 0) { // tell blocked writers to wake up + _linda->readHappened.notify_all(); + } else { + raise_luaL_error(L_, "unknown wake hint '%s'", _who); + } + return 0; +} - // make sure the key is of a valid type - check_key_types(L_, _key_i, _key_i); +// ################################################################################################# - STACK_GROW(L_, 1); +/* + * string = linda:__concat( a, b) + * + * Return the concatenation of a pair of items, one of them being a linda + * + * Useful for concatenation or debugging purposes + */ +LUAG_FUNC(linda_concat) +{ // L_: linda1? linda2? + bool _atLeastOneLinda{ false }; + // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. + if (LindaToString(L_, 1)) { + _atLeastOneLinda = true; + lua_replace(L_, 1); + } + if (LindaToString(L_, 2)) { + _atLeastOneLinda = true; + lua_replace(L_, 2); + } + if (!_atLeastOneLinda) { // should not be possible + raise_luaL_error(L_, "internal error: linda_concat called on non-Linda"); + } + lua_concat(L_, 2); + return 1; +} - // make sure there is something to send - if (lua_gettop(L_) == _key_i) { - raise_luaL_error(L_, "no data to send"); - } +// ################################################################################################# - // convert nils to some special non-nil sentinel in sent values - keeper_toggle_nil_sentinels(L_, _key_i + 1, LookupMode::ToKeeper); - bool _ret{ false }; - CancelRequest _cancel{ CancelRequest::None }; - KeeperCallResult _pushed; - { - Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue(L_) }; - Keeper* const _K{ _linda->whichKeeper() }; - KeeperState const _KL{ _K ? _K->L : nullptr }; - if (_KL == nullptr) - return 0; +/* + * [val] = linda_count( linda_ud, [key [, ...]]) + * + * Get a count of the pending elements in the specified keys + */ +LUAG_FUNC(linda_count) +{ + auto _count = [](lua_State* L_) { + Linda* const _linda{ ToLinda(L_, 1) }; + // make sure the keys are of a valid type + check_key_types(L_, 2, lua_gettop(L_)); - STACK_CHECK_START_REL(_KL, 0); - for (bool _try_again{ true };;) { - if (_lane != nullptr) { - _cancel = _lane->cancelRequest; - } - _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest; - // if user wants to cancel, or looped because of a timeout, the call returns without sending anything - if (!_try_again || _cancel != CancelRequest::None) { - _pushed.emplace(0); - break; - } + Keeper* const _K{ _linda->whichKeeper() }; + KeeperCallResult const _pushed{ keeper_call(_K->L, KEEPER_API(count), L_, _linda, 2) }; + return OptionalValue(_pushed, L_, "tried to count an invalid key"); + }; + return Linda::ProtectedCall(L_, _count); +} - STACK_CHECK(_KL, 0); - _pushed = keeper_call(_KL, KEEPER_API(send), L_, _linda, _key_i); - if (!_pushed.has_value()) { - break; - } - LUA_ASSERT(L_, _pushed.value() == 1); +// ################################################################################################# - _ret = lua_toboolean(L_, -1) ? true : false; - lua_pop(L_, 1); +/* + * lightuserdata= linda_deep( linda_ud ) + * + * Return the 'deep' userdata pointer, identifying the Linda. + * + * This is needed for using Lindas as key indices (timer system needs it); + * separately created proxies of the same underlying deep object will have + * different userdata and won't be known to be essentially the same deep one + * without this. + */ +LUAG_FUNC(linda_deep) +{ + Linda* const _linda{ ToLinda(L_, 1) }; + lua_pushlightuserdata(L_, _linda); // just the address + return 1; +} - if (_ret) { - // Wake up ALL waiting threads - _linda->writeHappened.notify_all(); - break; - } +// ################################################################################################# - // instant timout to bypass the wait syscall - if (std::chrono::steady_clock::now() >= _until) { - break; /* no wait; instant timeout */ - } +/* + * table = linda:dump() + * return a table listing all pending data inside the linda + */ +LUAG_FUNC(linda_dump) +{ + auto _dump = [](lua_State* L_) { + Linda* const _linda{ ToLinda(L_, 1) }; + return keeper_push_linda_storage(*_linda, DestState{ L_ }); + }; + return Linda::ProtectedCall(L_, _dump); +} - // storage limit hit, wait until timeout or signalled that we should try again - { - Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings - if (_lane != nullptr) { - // change status of lane to "waiting" - _prev_status = _lane->status; // Running, most likely - LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case - _lane->status = Lane::Waiting; - LUA_ASSERT(L_, _lane->waiting_on == nullptr); - _lane->waiting_on = &_linda->readHappened; - } - // could not send because no room: wait until some data was read before trying again, or until timeout is reached - std::unique_lock _keeper_lock{ _K->mutex, std::adopt_lock }; - std::cv_status const status{ _linda->readHappened.wait_until(_keeper_lock, _until) }; - _keeper_lock.release(); // we don't want to release the lock! - _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups - if (_lane != nullptr) { - _lane->waiting_on = nullptr; - _lane->status = _prev_status; - } - } +// ################################################################################################# + +/* + * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) + * + * Get one or more values from Linda. + */ +LUAG_FUNC(linda_get) +{ + auto get = [](lua_State* L_) { + Linda* const _linda{ ToLinda(L_, 1) }; + 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) + check_key_types(L_, 2, 2); + + KeeperCallResult _pushed; + if (_linda->cancelRequest == CancelRequest::None) { + Keeper* const _K{ _linda->whichKeeper() }; + _pushed = keeper_call(_K->L, KEEPER_API(get), L_, _linda, 2); + if (_pushed.value_or(0) > 0) { + keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - _pushed.value(), LookupMode::FromKeeper); } - STACK_CHECK(_KL, 0); + } else { // linda is cancelled + // do nothing and return lanes.cancel_error + kCancelError.pushKey(L_); + _pushed.emplace(1); } + // an error can be raised if we attempt to read an unregistered function + return OptionalValue(_pushed, L_, "tried to copy unsupported types"); + }; + return Linda::ProtectedCall(L_, get); +} - if (!_pushed.has_value()) { - raise_luaL_error(L_, "tried to copy unsupported types"); +// ################################################################################################# + +/* + * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) + * + * Set limit to 1 Linda keys. + * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so + * Limit can be 0 to completely block everything + */ +LUAG_FUNC(linda_limit) +{ + auto _limit = [](lua_State* L_) { + Linda* const _linda{ ToLinda(L_, 1) }; + // make sure we got 3 arguments: the linda, a key and a limit + luaL_argcheck(L_, lua_gettop(L_) == 3, 2, "wrong number of arguments"); + // make sure we got a numeric limit + lua_Number const _limit{ luaL_checknumber(L_, 3) }; + if (_limit < 1) { + raise_luaL_argerror(L_, 3, "limit must be >= 0"); } + // make sure the key is of a valid type + check_key_types(L_, 2, 2); - switch (_cancel) { - case CancelRequest::Soft: - // if user wants to soft-cancel, the call returns lanes.cancel_error + KeeperCallResult _pushed; + if (_linda->cancelRequest == CancelRequest::None) { + Keeper* const _K{ _linda->whichKeeper() }; + _pushed = keeper_call(_K->L, KEEPER_API(limit), L_, _linda, 2); + LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 0 || _pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads + if (_pushed.value() == 1) { + LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1); + _linda->readHappened.notify_all(); // To be done from within the 'K' locking area + } + } else { // linda is cancelled + // do nothing and return lanes.cancel_error kCancelError.pushKey(L_); - return 1; - - case CancelRequest::Hard: - // raise an error interrupting execution only in case of hard cancel - raise_cancel_error(L_); // raises an error and doesn't return - - default: - lua_pushboolean(L_, _ret); // true (success) or false (timeout) - return 1; + _pushed.emplace(1); } + // propagate pushed boolean if any + return _pushed.value(); }; - return Linda::ProtectedCall(L_, _send); + return Linda::ProtectedCall(L_, _limit); } // ################################################################################################# @@ -432,33 +492,164 @@ LUAG_FUNC(linda_receive) break; } - if (std::chrono::steady_clock::now() >= _until) { - break; /* instant timeout */ - } + if (std::chrono::steady_clock::now() >= _until) { + break; /* instant timeout */ + } + + // nothing received, wait until timeout or signalled that we should try again + { + Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings + if (_lane != nullptr) { + // change status of lane to "waiting" + _prev_status = _lane->status; // Running, most likely + LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case + _lane->status = Lane::Waiting; + LUA_ASSERT(L_, _lane->waiting_on == nullptr); + _lane->waiting_on = &_linda->writeHappened; + } + // not enough data to read: wakeup when data was sent, or when timeout is reached + std::unique_lock _keeper_lock{ _K->mutex, std::adopt_lock }; + std::cv_status const _status{ _linda->writeHappened.wait_until(_keeper_lock, _until) }; + _keeper_lock.release(); // we don't want to release the lock! + _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups + if (_lane != nullptr) { + _lane->waiting_on = nullptr; + _lane->status = _prev_status; + } + } + } + STACK_CHECK(_KL, 0); + + if (!_pushed.has_value()) { + raise_luaL_error(L_, "tried to copy unsupported types"); + } + + switch (_cancel) { + case CancelRequest::Soft: + // if user wants to soft-cancel, the call returns kCancelError + kCancelError.pushKey(L_); + return 1; + + case CancelRequest::Hard: + // raise an error interrupting execution only in case of hard cancel + raise_cancel_error(L_); // raises an error and doesn't return + + default: + return _pushed.value(); + } + }; + return Linda::ProtectedCall(L_, _receive); +} + +// ################################################################################################# + +/* + * bool= linda:linda_send([timeout_secs=nil,] key_num|str|bool|lightuserdata, ...) + * + * Send one or more values to a Linda. If there is a limit, all values must fit. + * + * Returns: 'true' if the value was queued + * 'false' for timeout (only happens when the queue size is limited) + * nil, kCancelError if cancelled + */ +LUAG_FUNC(linda_send) +{ + auto _send = [](lua_State* L_) { + Linda* const _linda{ ToLinda(L_, 1) }; + int _key_i{ 2 }; // index of first key, if timeout not there + + std::chrono::time_point _until{ std::chrono::time_point::max() }; + if (lua_type(L_, 2) == LUA_TNUMBER) { // we don't want to use lua_isnumber() because of autocoercion + lua_Duration const _duration{ lua_tonumber(L_, 2) }; + if (_duration.count() >= 0.0) { + _until = std::chrono::steady_clock::now() + std::chrono::duration_cast(_duration); + } else { + raise_luaL_argerror(L_, 2, "duration cannot be < 0"); + } + ++_key_i; + } else if (lua_isnil(L_, 2)) { // alternate explicit "infinite timeout" by passing nil before the key + ++_key_i; + } + + // make sure the key is of a valid type + check_key_types(L_, _key_i, _key_i); + + STACK_GROW(L_, 1); + + // make sure there is something to send + if (lua_gettop(L_) == _key_i) { + raise_luaL_error(L_, "no data to send"); + } + + // convert nils to some special non-nil sentinel in sent values + keeper_toggle_nil_sentinels(L_, _key_i + 1, LookupMode::ToKeeper); + bool _ret{ false }; + CancelRequest _cancel{ CancelRequest::None }; + KeeperCallResult _pushed; + { + Lane* const _lane{ kLanePointerRegKey.readLightUserDataValue(L_) }; + Keeper* const _K{ _linda->whichKeeper() }; + KeeperState const _KL{ _K ? _K->L : nullptr }; + if (_KL == nullptr) + return 0; + + STACK_CHECK_START_REL(_KL, 0); + for (bool _try_again{ true };;) { + if (_lane != nullptr) { + _cancel = _lane->cancelRequest; + } + _cancel = (_cancel != CancelRequest::None) ? _cancel : _linda->cancelRequest; + // if user wants to cancel, or looped because of a timeout, the call returns without sending anything + if (!_try_again || _cancel != CancelRequest::None) { + _pushed.emplace(0); + break; + } + + STACK_CHECK(_KL, 0); + _pushed = keeper_call(_KL, KEEPER_API(send), L_, _linda, _key_i); + if (!_pushed.has_value()) { + break; + } + LUA_ASSERT(L_, _pushed.value() == 1); + + _ret = lua_toboolean(L_, -1) ? true : false; + lua_pop(L_, 1); - // nothing received, wait until timeout or signalled that we should try again - { - Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings - if (_lane != nullptr) { - // change status of lane to "waiting" - _prev_status = _lane->status; // Running, most likely - LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case - _lane->status = Lane::Waiting; - LUA_ASSERT(L_, _lane->waiting_on == nullptr); - _lane->waiting_on = &_linda->writeHappened; + if (_ret) { + // Wake up ALL waiting threads + _linda->writeHappened.notify_all(); + break; } - // not enough data to read: wakeup when data was sent, or when timeout is reached - std::unique_lock _keeper_lock{ _K->mutex, std::adopt_lock }; - std::cv_status const _status{ _linda->writeHappened.wait_until(_keeper_lock, _until) }; - _keeper_lock.release(); // we don't want to release the lock! - _try_again = (_status == std::cv_status::no_timeout); // detect spurious wakeups - if (_lane != nullptr) { - _lane->waiting_on = nullptr; - _lane->status = _prev_status; + + // instant timout to bypass the wait syscall + if (std::chrono::steady_clock::now() >= _until) { + break; /* no wait; instant timeout */ + } + + // storage limit hit, wait until timeout or signalled that we should try again + { + Lane::Status _prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings + if (_lane != nullptr) { + // change status of lane to "waiting" + _prev_status = _lane->status; // Running, most likely + LUA_ASSERT(L_, _prev_status == Lane::Running); // but check, just in case + _lane->status = Lane::Waiting; + LUA_ASSERT(L_, _lane->waiting_on == nullptr); + _lane->waiting_on = &_linda->readHappened; + } + // could not send because no room: wait until some data was read before trying again, or until timeout is reached + std::unique_lock _keeper_lock{ _K->mutex, std::adopt_lock }; + std::cv_status const status{ _linda->readHappened.wait_until(_keeper_lock, _until) }; + _keeper_lock.release(); // we don't want to release the lock! + _try_again = (status == std::cv_status::no_timeout); // detect spurious wakeups + if (_lane != nullptr) { + _lane->waiting_on = nullptr; + _lane->status = _prev_status; + } } } + STACK_CHECK(_KL, 0); } - STACK_CHECK(_KL, 0); if (!_pushed.has_value()) { raise_luaL_error(L_, "tried to copy unsupported types"); @@ -466,7 +657,7 @@ LUAG_FUNC(linda_receive) switch (_cancel) { case CancelRequest::Soft: - // if user wants to soft-cancel, the call returns kCancelError + // if user wants to soft-cancel, the call returns lanes.cancel_error kCancelError.pushKey(L_); return 1; @@ -475,10 +666,11 @@ LUAG_FUNC(linda_receive) raise_cancel_error(L_); // raises an error and doesn't return default: - return _pushed.value(); + lua_pushboolean(L_, _ret); // true (success) or false (timeout) + return 1; } }; - return Linda::ProtectedCall(L_, _receive); + return Linda::ProtectedCall(L_, _send); } // ################################################################################################# @@ -533,154 +725,6 @@ LUAG_FUNC(linda_set) // ################################################################################################# -/* - * [val] = linda_count( linda_ud, [key [, ...]]) - * - * Get a count of the pending elements in the specified keys - */ -LUAG_FUNC(linda_count) -{ - auto _count = [](lua_State* L_) { - Linda* const _linda{ ToLinda(L_, 1) }; - // make sure the keys are of a valid type - check_key_types(L_, 2, lua_gettop(L_)); - - Keeper* const _K{ _linda->whichKeeper() }; - KeeperCallResult const _pushed{ keeper_call(_K->L, KEEPER_API(count), L_, _linda, 2) }; - return OptionalValue(_pushed, L_, "tried to count an invalid key"); - }; - return Linda::ProtectedCall(L_, _count); -} - -// ################################################################################################# - -/* - * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) - * - * Get one or more values from Linda. - */ -LUAG_FUNC(linda_get) -{ - auto get = [](lua_State* L_) { - Linda* const _linda{ ToLinda(L_, 1) }; - 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) - check_key_types(L_, 2, 2); - - KeeperCallResult _pushed; - if (_linda->cancelRequest == CancelRequest::None) { - Keeper* const _K{ _linda->whichKeeper() }; - _pushed = keeper_call(_K->L, KEEPER_API(get), L_, _linda, 2); - if (_pushed.value_or(0) > 0) { - keeper_toggle_nil_sentinels(L_, lua_gettop(L_) - _pushed.value(), LookupMode::FromKeeper); - } - } else { // linda is cancelled - // do nothing and return lanes.cancel_error - kCancelError.pushKey(L_); - _pushed.emplace(1); - } - // an error can be raised if we attempt to read an unregistered function - return OptionalValue(_pushed, L_, "tried to copy unsupported types"); - }; - return Linda::ProtectedCall(L_, get); -} - -// ################################################################################################# - -/* - * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, int) - * - * Set limit to 1 Linda keys. - * Optionally wake threads waiting to write on the linda, in case the limit enables them to do so - * Limit can be 0 to completely block everything - */ -LUAG_FUNC(linda_limit) -{ - auto _limit = [](lua_State* L_) { - Linda* const _linda{ ToLinda(L_, 1) }; - // make sure we got 3 arguments: the linda, a key and a limit - luaL_argcheck(L_, lua_gettop(L_) == 3, 2, "wrong number of arguments"); - // make sure we got a numeric limit - lua_Number const _limit{ luaL_checknumber(L_, 3) }; - if (_limit < 1) { - raise_luaL_argerror(L_, 3, "limit must be >= 0"); - } - // make sure the key is of a valid type - check_key_types(L_, 2, 2); - - KeeperCallResult _pushed; - if (_linda->cancelRequest == CancelRequest::None) { - Keeper* const _K{ _linda->whichKeeper() }; - _pushed = keeper_call(_K->L, KEEPER_API(limit), L_, _linda, 2); - LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 0 || _pushed.value() == 1)); // no error, optional boolean value saying if we should wake blocked writer threads - if (_pushed.value() == 1) { - LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TBOOLEAN && lua_toboolean(L_, -1) == 1); - _linda->readHappened.notify_all(); // To be done from within the 'K' locking area - } - } else { // linda is cancelled - // do nothing and return lanes.cancel_error - kCancelError.pushKey(L_); - _pushed.emplace(1); - } - // propagate pushed boolean if any - return _pushed.value(); - }; - return Linda::ProtectedCall(L_, _limit); -} - -// ################################################################################################# - -/* - * (void) = linda_cancel( linda_ud, "read"|"write"|"both"|"none") - * - * Signal linda so that waiting threads wake up as if their own lane was cancelled - */ -LUAG_FUNC(linda_cancel) -{ - Linda* const _linda{ ToLinda(L_, 1) }; - char const* _who{ luaL_optstring(L_, 2, "both") }; - // make sure we got 3 arguments: the linda, a key and a limit - luaL_argcheck(L_, lua_gettop(L_) <= 2, 2, "wrong number of arguments"); - - _linda->cancelRequest = CancelRequest::Soft; - if (strcmp(_who, "both") == 0) { // tell everyone writers to wake up - _linda->writeHappened.notify_all(); - _linda->readHappened.notify_all(); - } else if (strcmp(_who, "none") == 0) { // reset flag - _linda->cancelRequest = CancelRequest::None; - } else if (strcmp(_who, "read") == 0) { // tell blocked readers to wake up - _linda->writeHappened.notify_all(); - } else if (strcmp(_who, "write") == 0) { // tell blocked writers to wake up - _linda->readHappened.notify_all(); - } else { - raise_luaL_error(L_, "unknown wake hint '%s'", _who); - } - return 0; -} - -// ################################################################################################# - -/* - * lightuserdata= linda_deep( linda_ud ) - * - * Return the 'deep' userdata pointer, identifying the Linda. - * - * This is needed for using Lindas as key indices (timer system needs it); - * separately created proxies of the same underlying deep object will have - * different userdata and won't be known to be essentially the same deep one - * without this. - */ -LUAG_FUNC(linda_deep) -{ - Linda* const _linda{ ToLinda(L_, 1) }; - lua_pushlightuserdata(L_, _linda); // just the address - return 1; -} - -// ################################################################################################# - LUAG_FUNC(linda_tostring) { return LindaToString(L_, 1); @@ -688,49 +732,6 @@ LUAG_FUNC(linda_tostring) // ################################################################################################# -/* - * string = linda:__concat( a, b) - * - * Return the concatenation of a pair of items, one of them being a linda - * - * Useful for concatenation or debugging purposes - */ -LUAG_FUNC(linda_concat) -{ // L_: linda1? linda2? - bool _atLeastOneLinda{ false }; - // Lua semantics enforce that one of the 2 arguments is a Linda, but not necessarily both. - if (LindaToString(L_, 1)) { - _atLeastOneLinda = true; - lua_replace(L_, 1); - } - if (LindaToString(L_, 2)) { - _atLeastOneLinda = true; - lua_replace(L_, 2); - } - if (!_atLeastOneLinda) { // should not be possible - raise_luaL_error(L_, "internal error: linda_concat called on non-Linda"); - } - lua_concat(L_, 2); - return 1; -} - -// ################################################################################################# - -/* - * table = linda:dump() - * return a table listing all pending data inside the linda - */ -LUAG_FUNC(linda_dump) -{ - auto _dump = [](lua_State* L_) { - Linda* const _linda{ ToLinda(L_, 1) }; - return keeper_push_linda_storage(*_linda, DestState{ L_ }); - }; - return Linda::ProtectedCall(L_, _dump); -} - -// ################################################################################################# - /* * table/string = linda:__towatch() * return a table listing all pending data inside the linda, or the stringified linda if empty @@ -769,6 +770,7 @@ namespace global { // but that's necessary to provide s_LindaMT without exposing it outside linda.cpp. /*static*/ LindaFactory LindaFactory::Instance{ global::sLindaMT }; +// ################################################################################################# // ################################################################################################# /* -- cgit v1.2.3-55-g6feb