Implement minetest.ipc_poll()

This commit is contained in:
sfan5 2024-05-28 23:04:08 +02:00
parent 72801d0233
commit d2b4c27f21
5 changed files with 61 additions and 0 deletions

@ -7077,6 +7077,15 @@ minetest.ipc_get("test:foo") -- returns an empty table
* `old_value`: value compared to using `==` (`nil` compares equal for non-existing keys)
* `new_value`: value that will be set
* returns: true on success, false otherwise
* `minetest.ipc_poll(key, timeout)`:
* Do a blocking wait until a value (other than `nil`) is present at the key.
* **IMPORTANT**: You usually don't need this function. Use this as a last resort
if nothing else can satisfy your use case! None of the Lua environments the
engine has are safe to block for extended periods, especially on the main
thread any delays directly translate to lag felt by players.
* `key`: as above
* `timeout`: maximum wait time, in milliseconds (positive values only)
* returns: true on success, false on timeout
Bans
----

@ -279,3 +279,18 @@ local function test_ipc_vector_preserve(cb)
assert(vector.check(v))
end
unittests.register("test_ipc_vector_preserve", test_ipc_vector_preserve)
local function test_ipc_poll(cb)
core.ipc_set("unittests:flag", nil)
assert(core.ipc_poll("unittests:flag", 1) == false)
-- Note that unlike the async result callback - which has to wait for the
-- next server step - the IPC is instant
local t0 = core.get_us_time()
core.handle_async(function()
core.ipc_set("unittests:flag", true)
end, function() end)
assert(core.ipc_poll("unittests:flag", 1000) == true, "Wait failed (or slow machine?)")
print("delta: " .. (core.get_us_time() - t0) .. "us")
end
unittests.register("test_ipc_poll", test_ipc_poll)

@ -6,6 +6,7 @@
#include "common/c_packer.h"
#include "server.h"
#include "debug.h"
#include <chrono>
typedef std::shared_lock<std::shared_mutex> SharedReadLock;
typedef std::unique_lock<std::shared_mutex> SharedWriteLock;
@ -54,6 +55,7 @@ int ModApiIPC::l_ipc_set(lua_State *L)
else
store->map.erase(key); // delete the map value for nil
}
store->signal();
return 0;
}
@ -89,10 +91,37 @@ int ModApiIPC::l_ipc_cas(lua_State *L)
store->map.erase(key);
}
}
if (ok)
store->signal();
lua_pushboolean(L, ok);
return 1;
}
int ModApiIPC::l_ipc_poll(lua_State *L)
{
auto *store = getGameDef(L)->getModIPCStore();
auto key = readParam<std::string>(L, 1);
auto timeout = std::chrono::milliseconds(
std::max<int>(0, luaL_checkinteger(L, 2))
);
bool ret;
{
SharedReadLock autolock(store->mutex);
// wait until value exists or timeout
ret = store->condvar.wait_for(autolock, timeout, [&] () -> bool {
return store->map.count(key) != 0;
});
}
lua_pushboolean(L, ret);
return 1;
}
/*
* Implementation note:
* Iterating over the IPC table is intentionally not supported.
@ -108,4 +137,5 @@ void ModApiIPC::Initialize(lua_State *L, int top)
API_FCT(ipc_get);
API_FCT(ipc_set);
API_FCT(ipc_cas);
API_FCT(ipc_poll);
}

@ -10,6 +10,7 @@ private:
static int l_ipc_get(lua_State *L);
static int l_ipc_set(lua_State *L);
static int l_ipc_cas(lua_State *L);
static int l_ipc_poll(lua_State *L);
public:
static void Initialize(lua_State *L, int top);

@ -48,6 +48,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <optional>
#include <string_view>
#include <shared_mutex>
#include <condition_variable>
class ChatEvent;
struct ChatEventChat;
@ -149,12 +150,17 @@ struct ModIPCStore {
/// RW lock for this entire structure
std::shared_mutex mutex;
/// Signalled on any changes to the map contents
std::condition_variable_any condvar;
/**
* Map storing the data
*
* @note Do not store `nil` data in this map, instead remove the whole key.
*/
std::unordered_map<std::string, std::unique_ptr<PackedValue>> map;
/// @note Should be called without holding the lock.
inline void signal() { condvar.notify_all(); }
};
class Server : public con::PeerHandler, public MapEventReceiver,