1
0
mirror of https://github.com/minetest/minetest.git synced 2024-12-27 19:20:33 +01:00

Implement minetest.ipc_cas()

This commit is contained in:
sfan5 2024-05-23 15:44:16 +02:00
parent f1a436619f
commit 72801d0233
5 changed files with 68 additions and 8 deletions

View File

@ -7066,6 +7066,18 @@ minetest.ipc_get("test:foo").subkey = "value" -- WRONG!
minetest.ipc_get("test:foo") -- returns an empty table
```
**Advanced**:
* `minetest.ipc_cas(key, old_value, new_value)`:
* Write a value to the shared data area, but only if the previous value
equals what was given.
This operation is called Compare-and-Swap and can be used to implement
synchronization between threads.
* `key`: as above
* `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
Bans
----

View File

@ -22,8 +22,11 @@ local function do_tests()
assert(core.registered_items["unittests:description_test"].on_place == true)
end
-- this is checked from the main env
core.ipc_set("unittests:mg", { pcall(do_tests) })
-- first thread to get here runs the tests
if core.ipc_cas("unittests:mg_once", nil, true) then
-- this is checked from the main env
core.ipc_set("unittests:mg", { pcall(do_tests) })
end
core.register_on_generated(function(vm, pos1, pos2, blockseed)
local n = tonumber(core.get_mapgen_setting("chunksize")) * 16 - 1

View File

@ -507,6 +507,7 @@ PackedValue *script_pack(lua_State *L, int idx)
void script_unpack(lua_State *L, PackedValue *pv)
{
assert(pv);
// table that tracks objects for keep_ref / PUSHREF (key = instr index)
lua_newtable(L);
const int top = lua_gettop(L);

View File

@ -10,6 +10,17 @@
typedef std::shared_lock<std::shared_mutex> SharedReadLock;
typedef std::unique_lock<std::shared_mutex> SharedWriteLock;
static inline auto read_pv(lua_State *L, int idx)
{
std::unique_ptr<PackedValue> ret;
if (!lua_isnil(L, idx)) {
ret.reset(script_pack(L, idx));
if (ret->contains_userdata)
throw LuaError("Userdata not allowed");
}
return ret;
}
int ModApiIPC::l_ipc_get(lua_State *L)
{
auto *store = getGameDef(L)->getModIPCStore();
@ -34,12 +45,7 @@ int ModApiIPC::l_ipc_set(lua_State *L)
auto key = readParam<std::string>(L, 1);
luaL_checkany(L, 2);
std::unique_ptr<PackedValue> pv;
if (!lua_isnil(L, 2)) {
pv.reset(script_pack(L, 2));
if (pv->contains_userdata)
throw LuaError("Userdata not allowed");
}
auto pv = read_pv(L, 2);
{
SharedWriteLock autolock(store->mutex);
@ -51,6 +57,42 @@ int ModApiIPC::l_ipc_set(lua_State *L)
return 0;
}
int ModApiIPC::l_ipc_cas(lua_State *L)
{
auto *store = getGameDef(L)->getModIPCStore();
auto key = readParam<std::string>(L, 1);
luaL_checkany(L, 2);
const int idx_old = 2;
luaL_checkany(L, 3);
auto pv_new = read_pv(L, 3);
bool ok = false;
{
SharedWriteLock autolock(store->mutex);
// unpack and compare old value
auto it = store->map.find(key);
if (it == store->map.end()) {
ok = lua_isnil(L, idx_old);
} else {
script_unpack(L, it->second.get());
ok = lua_equal(L, idx_old, -1);
lua_pop(L, 1);
}
// put new value
if (ok) {
if (pv_new)
store->map[key] = std::move(pv_new);
else
store->map.erase(key);
}
}
lua_pushboolean(L, ok);
return 1;
}
/*
* Implementation note:
* Iterating over the IPC table is intentionally not supported.
@ -65,4 +107,5 @@ void ModApiIPC::Initialize(lua_State *L, int top)
API_FCT(ipc_get);
API_FCT(ipc_set);
API_FCT(ipc_cas);
}

View File

@ -9,6 +9,7 @@ class ModApiIPC : public ModApiBase {
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);
public:
static void Initialize(lua_State *L, int top);