Skip to content

Commit

Permalink
Implement minetest.ipc_poll()
Browse files Browse the repository at this point in the history
  • Loading branch information
sfan5 committed May 28, 2024
1 parent 4c2f309 commit 891ecbe
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 0 deletions.
9 changes: 9 additions & 0 deletions doc/lua_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6927,6 +6927,15 @@ minetest.ipc_get("test:foo") -- returns an empty table
* `old_value`: value compared against (`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
----
Expand Down
13 changes: 13 additions & 0 deletions games/devtest/mods/unittests/misc.lua
Original file line number Diff line number Diff line change
Expand Up @@ -279,3 +279,16 @@ 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 unless the async result callback - which has to wait for the
-- next server step - the IPC is instant
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?)")
end
unittests.register("test_ipc_poll", test_ipc_poll)
27 changes: 27 additions & 0 deletions src/script/lua_api/l_ipc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -53,6 +54,7 @@ int ModApiIPC::l_ipc_set(lua_State *L)
store->map[key] = std::move(pv);
else
store->map.erase(key); // delete the map value for nil
store->signal();
}
return 0;
}
Expand Down Expand Up @@ -87,12 +89,36 @@ int ModApiIPC::l_ipc_cas(lua_State *L)
store->map[key] = std::move(pv_new);
else
store->map.erase(key);
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);

int timeout = std::max<int>(0, luaL_checkinteger(L, 2));
auto end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout);

bool ret;
{
SharedReadLock autolock(store->mutex);

// wait until value exists or timeout
ret = store->condvar.wait_until(store->mutex, end_time, [&] () -> bool {
return store->map.count(key) != 0;
});
}

lua_pushboolean(L, ret);
return 1;
}

/*
* Implementation note:
* Iterating over the IPC table is intentionally not supported.
Expand All @@ -108,4 +134,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);
}
1 change: 1 addition & 0 deletions src/script/lua_api/l_ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class ModApiIPC : public ModApiBase {
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);
Expand Down
5 changes: 5 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,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;
Expand Down Expand Up @@ -145,12 +146,16 @@ struct ClientInfo {
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;

inline void signal() { condvar.notify_all(); }
};

class Server : public con::PeerHandler, public MapEventReceiver,
Expand Down

0 comments on commit 891ecbe

Please sign in to comment.