From dddc28153796f9c8eb256eddb335c8643226fd0b Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Wed, 12 Jun 2024 18:18:24 +0200 Subject: linda :get(), :set(), :limit() return value changes --- src/keeper.cpp | 47 +++++++++++++++++++++++++++-------------------- src/lanes.lua | 41 ++++++++++++++++++++++++++--------------- src/linda.cpp | 38 +++++++++++++++++++------------------- 3 files changed, 72 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/keeper.cpp b/src/keeper.cpp index 7000372..4175d84 100644 --- a/src/keeper.cpp +++ b/src/keeper.cpp @@ -123,26 +123,32 @@ KeyUD* KeyUD::GetPtr(KeeperState const K_, int idx_) // ################################################################################################# // in: fifo -// out: ...|nothing -// pops the fifo, push as much data as is available (up to the specified count) without consuming it +// out: bool ... +// pops the fifo, push bool + as much data as is available (up to the specified count) without consuming it +// bool is true if the requested count was served, else false void KeyUD::peek(KeeperState const K_, int const count_) { - LUA_ASSERT(K_, KeyUD::GetPtr(K_, -1) == this); + STACK_CHECK_START_ABS(K_, 1); + LUA_ASSERT(K_, KeyUD::GetPtr(K_, -1) == this); // K_: KeyUD if (count <= 0) { // no data is available lua_pop(K_, 1); // K_: + lua_pushinteger(K_, 0); // K_: 0 return; } - // read value off the fifo + // read value off the fifo, if possible prepareAccess(K_, -1); // K_: fifo - int const _at{ lua_gettop(K_) }; int const _count{ std::min(count_, count) }; + lua_pushinteger(K_, _count); // K_: fifo _count + lua_insert(K_, 1); // K_: _count fifo + STACK_CHECK(K_, 2); STACK_GROW(K_, _count); for (int const _i : std::ranges::iota_view{ 1, _count }) { // push val2 to valN - lua_rawgeti(K_, 1, first + _i); // K_: fifo val2..N + lua_rawgeti(K_, 2, first + _i); // K_: _count fifo val2..N } - lua_rawgeti(K_, 1, first); // push val1 // K_: fifo val2..N val1 - lua_replace(K_, _at); // replace fifo by val1 to get the output properly ordered // K_: val1..N + lua_rawgeti(K_, 2, first); // push val1 // K_: _count fifo val2..N val1 + lua_replace(K_, 2); // replace fifo by val1 to get the output properly ordered // K_: _count val1..N + STACK_CHECK(K_, 1 + _count); } // ################################################################################################# @@ -418,7 +424,7 @@ int keepercall_destruct(lua_State* const L_) // ################################################################################################# // in: linda_ud key [count] -// out: at most values +// out: bool + at most values int keepercall_get(lua_State* const L_) { KeeperState const _K{ L_ }; @@ -433,11 +439,13 @@ int keepercall_get(lua_State* const L_) lua_remove(_K, 1); // _K: KeyUD KeyUD* const _key{ KeyUD::GetPtr(_K, -1) }; if (_key != nullptr) { - _key->peek(_K, _count); // _K: val... + _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: + lua_pushinteger(_K, 0); // _K: 0 } + LUA_ASSERT(_K, lua_isnumber(_K, 1)); return lua_gettop(_K); } @@ -462,13 +470,10 @@ int keepercall_limit(lua_State* const L_) } // remove any clutter on the stack lua_settop(_K, 0); // _K: - if (_key->changeLimit(_limit)) { - // 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 - lua_pushboolean(_K, 1); // _K: true - } - // return 0 or 1 value - return lua_gettop(_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 + lua_pushboolean(_K, _key->changeLimit(_limit) ? 1 : 0); // _K: bool + return 1; } // ################################################################################################# @@ -570,7 +575,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 nothing +// out: true if the linda was full but it's no longer the case, else false int keepercall_set(lua_State* const L_) { KeeperState const _K{ L_ }; @@ -593,7 +598,7 @@ int keepercall_set(lua_State* const L_) lua_pushnil(_K); // _K: KeysDB key nil lua_rawset(_K, -3); // _K: KeysDB } else { - lua_remove(_K, -2); // _K: KeysDB KeyUD + lua_remove(_K, -2); // KeyUD::reset expects KeyUD at the top // _K: KeysDB KeyUD // we create room if the KeyUD was full but it is no longer the case _should_wake_writers = _key->reset(_K); } @@ -619,7 +624,9 @@ int keepercall_set(lua_State* const L_) lua_replace(_K, -2 - _count); // _K: KeysDB KeyUD val... [[maybe_unused]] bool const _pushed{ _key->push(_K, _count) }; // _K: KeysDB } - return _should_wake_writers ? (lua_pushboolean(_K, 1), 1) : 0; + // stack isn't the same here depending on what we did before, but that's not a problem + lua_pushboolean(_K, _should_wake_writers ? 1 : 0); // _K: ... bool + return 1; } // ################################################################################################# diff --git a/src/lanes.lua b/src/lanes.lua index d890adf..c860ba0 100644 --- a/src/lanes.lua +++ b/src/lanes.lua @@ -400,7 +400,8 @@ 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" - local first_time = timerLinda:get(first_time_key) == nil + local _, _first_time_val = timerLinda:get(first_time_key) + local first_time = (_first_time_val == nil) timerLinda:set(first_time_key, true) if first_time then @@ -490,8 +491,8 @@ local configure_timers = function() -- local t2 = t1[key_] if not t2 then - t2= {} - t1[key_]= t2 + t2 = {} + t1[key_] = t2 end t2[1] = wakeup_at_ @@ -507,10 +508,10 @@ local configure_timers = function() local now = now_secs() local next_wakeup - for linda_deep,t1 in pairs(collection) do - for key,t2 in pairs(t1) do + for linda_deep, t1 in pairs(collection) do + for key, t2 in pairs(t1) do -- - if key==linda_deep then + if key == linda_deep then -- no 'continue' in Lua :/ else -- 't2': { wakeup_at_secs [,period_secs] } @@ -519,10 +520,10 @@ local configure_timers = function() local period= t2[2] -- may be 'nil' if wakeup_at <= now then - local linda= t1[linda_deep] + local linda = t1[linda_deep] assert(linda) - linda:set(key, now ) + linda:set(key, now) -- 'pairs()' allows the values to be modified (and even -- removed) as far as keys are not touched @@ -530,13 +531,13 @@ local configure_timers = function() if not period then -- one-time timer; gone -- - t1[key]= nil - wakeup_at= nil -- no 'continue' in Lua :/ + t1[key] = nil + wakeup_at = nil -- no 'continue' in Lua :/ else -- repeating timer; find next wakeup (may jump multiple repeats) -- repeat - wakeup_at= wakeup_at+period + wakeup_at= wakeup_at+period until wakeup_at > now t2[1]= wakeup_at @@ -544,7 +545,7 @@ local configure_timers = function() end if wakeup_at and ((not next_wakeup) or (wakeup_at < next_wakeup)) then - next_wakeup= wakeup_at + next_wakeup = wakeup_at end end end -- t2 loop @@ -675,7 +676,12 @@ local cancel_error local genlock = function(linda_, key_, N) -- clear existing data and set the limit N = N or 1 - if linda_:set(key_) == cancel_error or linda_:limit(key_, N) == cancel_error then + local _status, _err = linda_:set(key_) + if _err == cancel_error then + return cancel_error + end + local _status, _err = linda_:limit(key_, N) + if _err == cancel_error then return cancel_error end @@ -723,7 +729,12 @@ end -- genlock -- local genatomic = function(linda_, key_, initial_val_) -- clears existing data (also queue). the slot may contain the stored value, and an additional boolean value - if linda_:limit(key_, 2) == cancel_error or linda_:set(key_, initial_val_ or 0.0) == cancel_error then + local _status, _err = linda_:limit(key_, 2) + if _err == cancel_error then + return cancel_error + end + local _status, _err = linda_:set(key_, initial_val_ or 0.0) + if _err == cancel_error then return cancel_error end @@ -734,7 +745,7 @@ local genatomic = function(linda_, key_, initial_val_) if _err == cancel_error then return cancel_error end - local val = linda_:get(key_) + local _, val = linda_:get(key_) if val ~= cancel_error then val = val + (diff_ or 1.0) -- set() releases the lock by emptying queue diff --git a/src/linda.cpp b/src/linda.cpp index 91138c5..316e917 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -393,7 +393,7 @@ LUAG_FUNC(linda_dump) // ################################################################################################# /* - * [val [, ...]] = linda_get( linda_ud, key_num|str|bool|lightuserdata [, count = 1]) + * count, [val [, ...]]|nil,cancel_error = linda:get(key_num|str|bool|lightuserdata [, count = 1]) * * Get one or more values from Linda. */ @@ -412,9 +412,10 @@ LUAG_FUNC(linda_get) Keeper* const _keeper{ _linda->whichKeeper() }; _pushed = keeper_call(_keeper->K, KEEPER_API(get), L_, _linda, 2); } else { // linda is cancelled - // do nothing and return lanes.cancel_error + // do nothing and return nil,lanes.cancel_error + lua_pushnil(L_); kCancelError.pushKey(L_); - _pushed.emplace(1); + _pushed.emplace(2); } // an error can be raised if we attempt to read an unregistered function return OptionalValue(_pushed, L_, "tried to copy unsupported types"); @@ -425,7 +426,7 @@ LUAG_FUNC(linda_get) // ################################################################################################# /* - * [true] = linda_limit( linda_ud, key_num|str|bool|lightuserdata, [int]) + * [bool]|nil,cancel_error = linda:limit(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 @@ -450,15 +451,15 @@ LUAG_FUNC(linda_limit) if (_linda->cancelRequest == CancelRequest::None) { Keeper* const _keeper{ _linda->whichKeeper() }; _pushed = keeper_call(_keeper->K, 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_, luaG_type(L_, -1) == LuaType::BOOLEAN && lua_toboolean(L_, -1) == 1); + LUA_ASSERT(L_, _pushed.has_value() && (_pushed.value() == 1) && luaG_type(L_, -1) == LuaType::BOOLEAN); // no error, boolean value saying if we should wake blocked writer threads + if (lua_toboolean(L_, -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 + // do nothing and return nil,lanes.cancel_error + lua_pushnil(L_); kCancelError.pushKey(L_); - _pushed.emplace(1); + _pushed.emplace(2); } // propagate pushed boolean if any return _pushed.value(); @@ -470,14 +471,13 @@ LUAG_FUNC(linda_limit) /* * 2 modes of operation - * [val, key]= linda_receive( linda_ud, [timeout_secs_num=nil], key_num|str|bool|lightuserdata [, ...] ) + * [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 * [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. * returns the actual consumed values, or nil if there weren't enough values to consume - * */ LUAG_FUNC(linda_receive) { @@ -763,7 +763,7 @@ LUAG_FUNC(linda_send) // ################################################################################################# /* - * [true|lanes.cancel_error] = linda_set( linda_ud, key_num|str|bool|lightuserdata [, value [, ...]]) + * [true|nil,lanes.cancel_error] = linda:set(key_num|str|bool|lightuserdata [, value [, ...]]) * * Set one or more value to Linda. Ignores limits. * @@ -773,7 +773,7 @@ LUAG_FUNC(linda_set) { auto set = [](lua_State* L_) { Linda* const _linda{ ToLinda(L_, 1) }; - bool const _has_value{ lua_gettop(L_) > 2 }; + bool const _has_data{ lua_gettop(L_) > 2 }; // make sure the key is of a valid type (throws an error if not the case) check_key_types(L_, 2, 2); @@ -782,22 +782,22 @@ LUAG_FUNC(linda_set) if (_linda->cancelRequest == CancelRequest::None) { _pushed = keeper_call(_keeper->K, KEEPER_API(set), L_, _linda, 2); if (_pushed.has_value()) { // no error? - LUA_ASSERT(L_, _pushed.value() == 0 || _pushed.value() == 1); + LUA_ASSERT(L_, _pushed.value() == 1 && luaG_type(L_, -1) == LuaType::BOOLEAN); - if (_has_value) { + if (_has_data) { // we put some data in the slot, tell readers that they should wake _linda->writeHappened.notify_all(); // To be done from within the 'K' locking area } - if (_pushed.value() == 1) { + if (lua_toboolean(L_, -1)) { // the key was full, but it is no longer the case, tell writers they should wake - LUA_ASSERT(L_, luaG_type(L_, -1) == LuaType::BOOLEAN && 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 + // do nothing and return nil,lanes.cancel_error + lua_pushnil(L_); kCancelError.pushKey(L_); - _pushed.emplace(1); + _pushed.emplace(2); } // must trigger any error after keeper state has been released -- cgit v1.2.3-55-g6feb