mirror of
https://github.com/minetest/minetest.git
synced 2024-12-28 11:40:30 +01:00
Implement minetest.ipc_cas()
This commit is contained in:
parent
f1a436619f
commit
72801d0233
@ -7066,6 +7066,18 @@ minetest.ipc_get("test:foo").subkey = "value" -- WRONG!
|
|||||||
minetest.ipc_get("test:foo") -- returns an empty table
|
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
|
Bans
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -22,8 +22,11 @@ local function do_tests()
|
|||||||
assert(core.registered_items["unittests:description_test"].on_place == true)
|
assert(core.registered_items["unittests:description_test"].on_place == true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- this is checked from the main env
|
-- first thread to get here runs the tests
|
||||||
core.ipc_set("unittests:mg", { pcall(do_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)
|
core.register_on_generated(function(vm, pos1, pos2, blockseed)
|
||||||
local n = tonumber(core.get_mapgen_setting("chunksize")) * 16 - 1
|
local n = tonumber(core.get_mapgen_setting("chunksize")) * 16 - 1
|
||||||
|
@ -507,6 +507,7 @@ PackedValue *script_pack(lua_State *L, int idx)
|
|||||||
|
|
||||||
void script_unpack(lua_State *L, PackedValue *pv)
|
void script_unpack(lua_State *L, PackedValue *pv)
|
||||||
{
|
{
|
||||||
|
assert(pv);
|
||||||
// table that tracks objects for keep_ref / PUSHREF (key = instr index)
|
// table that tracks objects for keep_ref / PUSHREF (key = instr index)
|
||||||
lua_newtable(L);
|
lua_newtable(L);
|
||||||
const int top = lua_gettop(L);
|
const int top = lua_gettop(L);
|
||||||
|
@ -10,6 +10,17 @@
|
|||||||
typedef std::shared_lock<std::shared_mutex> SharedReadLock;
|
typedef std::shared_lock<std::shared_mutex> SharedReadLock;
|
||||||
typedef std::unique_lock<std::shared_mutex> SharedWriteLock;
|
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)
|
int ModApiIPC::l_ipc_get(lua_State *L)
|
||||||
{
|
{
|
||||||
auto *store = getGameDef(L)->getModIPCStore();
|
auto *store = getGameDef(L)->getModIPCStore();
|
||||||
@ -34,12 +45,7 @@ int ModApiIPC::l_ipc_set(lua_State *L)
|
|||||||
auto key = readParam<std::string>(L, 1);
|
auto key = readParam<std::string>(L, 1);
|
||||||
|
|
||||||
luaL_checkany(L, 2);
|
luaL_checkany(L, 2);
|
||||||
std::unique_ptr<PackedValue> pv;
|
auto pv = read_pv(L, 2);
|
||||||
if (!lua_isnil(L, 2)) {
|
|
||||||
pv.reset(script_pack(L, 2));
|
|
||||||
if (pv->contains_userdata)
|
|
||||||
throw LuaError("Userdata not allowed");
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
SharedWriteLock autolock(store->mutex);
|
SharedWriteLock autolock(store->mutex);
|
||||||
@ -51,6 +57,42 @@ int ModApiIPC::l_ipc_set(lua_State *L)
|
|||||||
return 0;
|
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:
|
* Implementation note:
|
||||||
* Iterating over the IPC table is intentionally not supported.
|
* 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_get);
|
||||||
API_FCT(ipc_set);
|
API_FCT(ipc_set);
|
||||||
|
API_FCT(ipc_cas);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ class ModApiIPC : public ModApiBase {
|
|||||||
private:
|
private:
|
||||||
static int l_ipc_get(lua_State *L);
|
static int l_ipc_get(lua_State *L);
|
||||||
static int l_ipc_set(lua_State *L);
|
static int l_ipc_set(lua_State *L);
|
||||||
|
static int l_ipc_cas(lua_State *L);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void Initialize(lua_State *L, int top);
|
static void Initialize(lua_State *L, int top);
|
||||||
|
Loading…
Reference in New Issue
Block a user